题解 P7114 【字符串匹配(洛谷民间数据)】
xtx1092515503 · · 题解
严格
首先,我们必须知道一个事实:
如果一个长度为
具体可以通过看一张图直观理解:
然后,我们考虑Z算法(即【模板】扩展 KMP(Z 函数))。
对于位置
回到本题。我们考虑枚举
则依据我们刚才的循环节理论,假如其可以被表示成
考虑
于是,只有
在下文中,定义“奇偶性”为某个串中出现奇数次的字符数量。
考虑截去长度为
更准确地说,
于是,我们会发现,共有
我们下面考虑有多少个合法的
于是,我们要考虑的,就是
我们可以先考虑使用桶进行维护所有前缀中各奇偶性的出现次数,每次暴力扫过桶处理。则目前的复杂度是
先贴出考场代码(会TLE):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n,Z[2001000],mas[2001000],sum[30],CN,BN;
ll res;
char s[2001000];
int COUNT(int ip){
int ret=0;
while(ip)ret+=ip&1,ip>>=1;
return ret;
}
int main(){
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%s",s),n=strlen(s),memset(sum,0,sizeof(sum)),res=CN=BN=0;
mas[0]=1<<(s[0]-'a');
for(int i=1;i<n;i++)mas[i]=mas[i-1]^(1<<(s[i]-'a'));
CN=COUNT(mas[n-1]);
int Centre=-1,Rpos=-1;
for(int i=1;i<n;i++){
if(i<=Rpos)Z[i]=min(Z[i-Centre],Rpos-i+1);
else Z[i]=0;
while(i+Z[i]<n&&s[Z[i]]==s[i+Z[i]])Z[i]++;
if(i+Z[i]>Rpos)Centre=i,Rpos=i+Z[i]-1;
}
// for(int i=1;i<n;i++)printf("%d ",Z[i]);puts("");
for(int i=1;i<n;i++){
int C1=mas[n-1]^mas[i-1];
C1=COUNT(C1);
int tot=min(Z[i],n-i-1)/i+1;
int N1=(tot+1)>>1,N2=(tot)>>1;
// printf("%d:%d %d\n",tot,N1,N2);
for(int j=0;j<=C1;j++)res+=1ll*sum[j]*N1;
sum[COUNT(mas[i-1])]++;
res+=1ll*BN*N2;
if(COUNT(mas[i-1])<=CN)BN++;
}
printf("%lld\n",res);
}
return 0;
}
下面考虑优化。
首先,上述程序统计奇偶性时,是使用了一个状压来统计——但这本身就有一个
然后,观察到随着
这两个优化加上,即可把复杂度优化到严格
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n,Z[2001000],cnt1[30],cnt2[30],sum[30],CN,BN,nown,sumn,nown2;
ll res;
char s[2001000];
int main(){
// freopen("string.in","r",stdin);
// freopen("string.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%s",s),n=strlen(s),memset(sum,0,sizeof(sum)),memset(cnt1,0,sizeof(cnt1)),memset(cnt2,0,sizeof(cnt2)),res=CN=BN=0;
for(int i=0;i<n;i++)cnt1[s[i]-'a']^=1;
for(int i=0;i<26;i++)CN+=cnt1[i];
int Centre=-1,Rpos=-1;
for(int i=1;i<n;i++){
if(i<=Rpos)Z[i]=min(Z[i-Centre],Rpos-i+1);
else Z[i]=0;
while(i+Z[i]<n&&s[Z[i]]==s[i+Z[i]])Z[i]++;
if(i+Z[i]>Rpos)Centre=i,Rpos=i+Z[i]-1;
}
// for(int i=1;i<n;i++)printf("%d ",Z[i]);puts("");
nown=CN,nown2=sumn=0;
for(int i=1;i<n;i++){
if(cnt1[s[i-1]-'a'])sumn-=sum[nown--];
else sumn+=sum[++nown];
cnt1[s[i-1]-'a']^=1;
int tot=min(Z[i],n-i-1)/i+1;
int N1=(tot+1)>>1,N2=(tot)>>1;
// printf("%d:%d %d\n",tot,N1,N2);
res+=1ll*sumn*N1;
if(cnt2[s[i-1]-'a'])nown2--;
else nown2++;
cnt2[s[i-1]-'a']^=1;
sum[nown2]++;
if(nown2<=nown)sumn++;
res+=1ll*BN*N2;
if(nown2<=CN)BN++;
}
printf("%lld\n",res);
}
return 0;
}