Mediocre String Problem (manacher+扩展kmp+差分)
题目大意:
给出,s1,s2, 挑一些 s1的字串+s2的前缀字串,看他们是不是回文串, len(subs1)>len(subs2)>1=1;
统计有多少个
- 首先 subs1是要和s2成回文,他的内部也要是回文,于是就分为2部分
- 求第一部分: 把s1翻转,然后来求一个扩展kmp,对于目标窜s2,求的是个数,那么求出的最大长度就有len个那个ans
- 然后看后面部分是不是回文窜,如何看有多少个? 就是看有一个回文串是以后面那个字母位开始(因为翻转了,就是前面一个字母,最后在翻回去)
- 利用manacher来求出这个东东
- 利用差分来来搞,因为求出的是最大的回文串,字串也要计入贡献
- 最后统计一下就ok了
- 详细情况看代码

#include <bits/stdc++.h> using namespace std; #define ri register int #define M 4000015 int n,m; string s1,s2; int nxt[M]; int et[M]; void getnxt() { int len=s2.length(); nxt[0]=len; int i=0; while(s2[i]==s2[i+1]&&i+1<len) i++;nxt[1]=i; int po=1; for(ri i=2;i<len;i++) { if(nxt[i-po]+i<po+nxt[po]) { nxt[i]=nxt[i-po]; } else { int j=po+nxt[po]-i; if(j<0) j=0; while(j<len&&s2[j]==s2[i+j]) j++;nxt[i]=j; po=i; } } } long long num[M]; long long nn[M]; string s; void getet() { for(ri i=s1.length()-1,j=0;i>=0;i--,j++) { s[j]=s1[i]; } int i=0; int l1=s1.length(),l2=s2.length(); while(s[i]==s2[i]&&i<l2&&i<l1) i++; et[0]=i; int po=0; for(ri i=1;i<l1;i++) { if(nxt[i-po]+i<po+et[po]) { et[i]=nxt[i-po]; } else { int j=po+et[po]-i; if(j<0) j=0; while(j<l2&&i+j<l1&&s[i+j]==s2[j]) j++;et[i]=j; po=i; } num[i-1]=et[i]; // cout<<num[i-1]<<" "; } for(ri i=s1.length()-1,j=0;i>=0;i--,j++) { nn[j]=num[i]; } } long long ans=0; int p[M]; void manacher() { s="$#"; for(ri i=0;i<s1.length();i++) { s+=s1[i]; s+="#"; } int mid=0,r=0; for(ri i=0;i<s1.length();i++) num[i]=0; for(ri i=0;i<s.length();i++) { if(i<r) { p[i]=min(r-i+1,p[2*mid-i]); } while(s[i-p[i]]==s[i+p[i]]) p[i]++; if(p[i]+i>r) r=p[i]+i-1,mid=i; int b=(i-p[i])/2; if((p[i]-1)&1) { int a=(i-p[i])/2; num[a]+=1; num[a+((p[i]-1)/2)+1]+=-1; } else { int a=(i-p[i])/2; num[a]+=1; num[(p[i]-1)/2+a]+=-1; } } } int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>s1>>s2; getnxt(); getet(); manacher(); long long arr=0; for(ri i=0;i<s1.length();i++) { arr+=num[i]; ans+=arr*nn[i]; } cout<<ans; }
后记:
- 为了方便,内存开4倍
- 注意在处理回文的时候,找他的真实位置(初始位置来确定,而不是 i
- p[i]-1 是0,可以直接跳过
- 思路就是,想思路,然后遇到新的问题,在此基础上想解决方案,而不是重新开始