題目鏈接
We call a string as a 0689-string if this string only consists of digits ‘0’, ‘6’, ‘8’ and ‘9’. Given a 0689-string of length , one must do the following operation exactly once: select a non-empty substring of and rotate it 180 degrees.
More formally, let be the -th character in string . After rotating the substring starting from and ending at 180 degrees (), string will become string of length extracted from the following equation, where indicates the -th character in string :
What’s the number of different strings one can get after the operation?
Input
There are multiple test cases. The first line of the input contains an integer , indicating the number of test cases. For each test case:
The first and only line contains a 0689-string ().
It’s guaranteed that the sum of of all test cases will not exceed .
Output
For each test case output one line containing one integer, indicating the number of different strings one can get after applying the operation exactly once.
Sample Input
2
0689
08
Sample Output
8
2
題目大意
一個字符串,僅有0、6、8、9組成,選取一個子串,翻轉,得到新的字符串,問新得到的字符串不同的有多少個?0,8翻轉還是自己,6翻轉是9,9翻轉是6。
思路
不要模擬去做,肯定超時。
如果每個數都不一樣,那麼翻轉後不同的應該是(1+n)*n/2,就是子串的個數,再算上自己本身的,是(1+n)*n/2+1,我們把重複的減去就行了。
去重,如果開頭-末位是0-0,8-8,6-9,9-6的都是不行的。比如0680,翻轉0680,其實還是翻轉68,和開頭結尾的0-0沒有啥關係。
0-0的情況,統計出字符串中0的個數num,那麼以0開頭並以0結尾子串的個數就是(1+num)*num/2。
8-8和0-0一樣。
9-6和6-9的情況,統計出字符串中9的個數x,字符串中6的個數y。6-9和9-6都是重複的,總重複的個數是x * y。
還有一個特例需要考慮,就是字符串全部爲6或者全部9,這時候沒有不翻轉(不變)的情況。如要減去剛開始(1+n)*n/2+1中加上的1。舉個栗子,字符串8,如果不算自己的那個了話,減去後就是0了,但是因爲8翻轉後是可以得到自己的,所以加上1,代表情況是自己。有0,有8,僅翻轉一個0或一個8是可以得到自己本身的;沒有0、8,只有6和9也是可以的,69在一起翻轉,還是自己。唯獨僅有6和僅有9的情況,翻轉後一定是得不到自己的,所以要減一。
需要注意爆int的問題,需要1ll或者(ll)轉化爲long long
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6+8;
char s[maxn];
ll num[4];
int main() {
int T; scanf("%d",&T);
while(T--){
scanf("%s",s);
int n = strlen(s);
memset(num,0,sizeof(num));
for(int i=0;i<n;i++){
if(s[i]=='0') num[0]++;
else if(s[i]=='6') num[1]++;
else if(s[i]=='8') num[2]++;
else if(s[i]=='9') num[3]++;
}
ll ans = (1ll+n)*n/2+1;
ans -= (1+num[0])*num[0]/2;
ans -= (1+num[2])*num[2]/2;
ans -= num[1]*num[3];
if(num[1]==n||num[3]==n) ans--;
printf("%lld\n",ans);
}
return 0;
}
另一種不是減去,而是加的方法,比較耗內存。
寫的時候遇到了算法一樣,但是超時的問題。
超時
- 用string s; cin>>s;可能會超時。用char* s[]; scanf("%s",s);
- memset的鍋:memset每次都要把num數組清空,T經過測試在5000以上,每次都清空num就會超時(本地只做memset也要很長時間),但是for初始化,n可能每次不是那麼大,不用全部清空。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6+8;
char s[maxn];
//string s;
int num[4][maxn];
int main() {
int T; scanf("%d",&T);
while(T--){
scanf("%s",s);
int n = strlen(s);
//cin>>s;超時
//int n = s.length();
for(int i=0;i<=n;i++) num[0][i]=num[1][i]=num[2][i]=num[3][i]=0;
//memset(num,0,sizeof(num)); 超時
for(int i=n-1;i>=0;i--){
num[0][i] = num[0][i+1] + (s[i]=='0');
num[1][i] = num[1][i+1] + (s[i]=='8');
num[2][i] = num[2][i+1] + (s[i]=='6');
num[3][i] = num[3][i+1] + (s[i]=='9');
}
ll ans = 1;
for(int i=0;i<n;i++){
if(s[i]=='0')
ans += num[1][i+1] + num[2][i+1] + num[3][i+1];
else if(s[i]=='8')
ans += num[0][i+1] + num[2][i+1] + num[3][i+1];
else if(s[i]=='6')
ans += num[0][i+1] + num[1][i+1] + num[2][i];
else if(s[i]=='9')
ans += num[0][i+1] + num[1][i+1] + num[3][i];
}
if(num[2][0]==n||num[3][0]==n) ans--;
printf("%lld\n",ans);
}
return 0;
}