反对称 Antisymmetry (manacher)(hash+二分)
对于一个 0/1 字符串,如果将这个字符串 0 和 1 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。比如 00001111 和 010101 就是反对称的,而 1001 就不是。
现在给出一个长度为 n的 0/1字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算。
这道题有两种做法
1:用hash加快判断,二分长度
2:用manacher的变形,改变判断条件
hash就不讲了
直接上代码:
#include<iostream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<cstdio> #include<cstdlib> #define base 10000000007 #define min(a,b) (a) > (b) ? (b) : (a) using namespace std; inline long long rd(){ long long x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void write(long long x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return ; } long long n; char a[1000006],s[1000006]; long long x[1000006],y[1000006]; long long po[1000006]; inline bool check(long long h,long long v){ if(v-h-1<0&&v==(n/2)||v+h+2>n+1&&v+1==(n/2)+1) return 0; long long sum1=x[v]-x[v-h-1]*po[h+1]; long long sum2=y[v+1]-y[v+h+2]*po[h+1]; //cout<<h<<" "<<sum1<<" "<<sum2<<endl; return (sum1==sum2); } int main(){ po[0]=1; for(long long i=1;i<=500006;i++) po[i]=po[i-1]*base; n=rd(); scanf("%s",a+1); for(long long i=1;i<=n;i++){ s[i]=a[i]-'0'; x[i]=x[i-1]*base+s[i]+1; } for(long long i=n;i>=1;i--) y[i]=y[i+1]*base+(s[i]^1)+1; long long ans=0; for(long long i=1;i<n;i++){ if(s[i]!=s[i+1]) ans++; else continue; long long set=0; long long l=0,r=min(i,n-i); while(l<=r){ long long mid=(l+r)/2; if(check(mid,i)){ l=mid+1; set=mid; } else r=mid-1; } ans+=set; } write(ans); return 0; }
然后来看如何改manacher
manacher是一种线性算法,用来求最长回文子串。不会的看这里:https://www.cnblogs.com/WWHHTT/p/10460258.html
然后改的时候将会问的判断条件改为一个0一个1对应,最后在将所有的中心位于#的p值除以二再相加即可 (因为反对称的字符串长度必须为偶数)
下面给出代码:
#include<iostream> #include<algorithm> #include<cstring> #include<string> #include<cstdio> #include<cstdlib> #include<cmath> using namespace std; inline long long rd(){ long long x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void write(long long x){ if(x<0) putchar('-'); if(x>9) write(x/10); putchar(x%10+'0'); return ; } char s[1000006]; char c[2000006]; long long n; long long p[2000006]; int main(){ n=rd(); scanf("%s",s+1); c[0]='$'; for(long long i=1;i<=n;i++){ c[i*2-1]='#'; c[i*2]=s[i]; } c[2*n+1]='#'; long long mx=0,id=0; long long ans=0; for(long long i=1;i<=2*n;i+=2){ if(mx>i) p[i]=min(p[2*id-i],mx-i); else p[i]=1; while((c[i+p[i]]==c[i-p[i]]&&c[i-p[i]]=='#')||(c[i+p[i]]=='0'&&c[i-p[i]]=='1')||(c[i-p[i]]=='0'&&c[i+p[i]]=='1')) p[i]++; if(i+p[i]>mx) mx=i+p[i],id=i; if(i%2) ans+=p[i]>>1; } /*for(long long i=1;i<=2*n;i++){ cout<<c[i]<<" "; } cout<<endl; for(long long i=1;i<=2*n;i++){ cout<<p[i]<<" "; }*/ write(ans); return 0; }
蒟蒻总是更懂你✿✿ヽ(°▽°)ノ✿