對於一個01字符串,如果將這個字符串0和1取反後,再將整個串反過來和原串一樣,就稱作“反對稱”字符串。比如00001111和010101就是反對稱的,1001就不是。
現在給出一個長度爲N的01字符串,求它有多少個子串是反對稱的。
第一行有個,第二行有個長度爲的字符串
輸出如題意
8
11001011
7
7個反對稱子串分別是:01(出現兩次),10(出現兩次),0101,1100和001011
對於一個字符串的位置而言
它向左和向右拓展k格若是一個迴文串,那麼拓展k-1格也必定是一個迴文串,我們只需要二分這個k,然後每次判斷是否爲迴文就可以了
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
string s; ll ans;
const int math = 11;
ll re[1000005], hash[1000005], hashback[1000005];
bool check(int x, int y, int midx)
{
int hash1 = hash[x + midx - 1] - hash[x - 1];
int hash2 = hashback[y + midx - 1] - hashback[y - 1];
if (x > y) {
swap(hash1, hash2);
swap(x, y);
}
hash1 *= re[y - x];
return hash1 == hash2;
}
int main()
{
int n;
cin >> n;
cin >> s;
re[0] = 1;
for (int i = 1; i <= n; ++i)
re[i] = re[i - 1] * math; //
for (int i = 1; i <= n; ++i)
hash[i] = hash[i - 1] + s[i - 1] * re[i]; //算哈希值
for (int i = 1; i <= n >> 1; ++i)
swap(s[i - 1], s[n - i]);
for (int i = 1; i <= n; ++i)
if (s[i - 1] == '0') s[i - 1] = '1';
else s[i - 1] = '0';//翻轉字符串
for (int i = 1; i <= n; ++i)
hashback[i] = hashback[i - 1] + s[i - 1] * re[i];//算翻轉後的字符串的哈希值
for (int i = 2; i <= n; ++i) {
int maxn=0;
int j = n - i + 2;
int l = 1, r = min(n - i + 1, n - j + 1);
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(i, j, mid))
maxn = max(maxn, mid),l=mid+1;
else r=mid-1;
}
ans += maxn;
}
printf("%lld", ans);
return 0;
}
其實我可以很快敲完代碼的。。。
就是被師兄要求的碼風坑了很久。。。