Problem
给出两个字符串集合 S 和 T,S中的所有字符串长度都为N,T中的所有字符串长度都为M,且N+M为偶数。
S中有
个字符串,T中有
个字符串。每次从 S 和 T 中各选一个串将它们拼接起来,问有多少种方案满足,把这个拼起来的串分成两半后,分成的两个串是循环同构串。
Hint
Solution
设w=n+m,表示某个S和T拼接起来的长度;设 ,表示分成两半后每一半的长度。
N>M
若N>M,则N>u,即S串会被那条分割线分成一个长度为u的较长的串以及一个长度为n-u的较短的串。还记那个较长的串为S,较短的串为a。
此时,题目就是要我们求a与某个T拼起来后,与S循环同构的方案数。
那么,我们可以让a在S上做一遍KMP,看看它能匹配到哪些位置,如图:
S中绿色格子表示被匹到的部分。我们可以发现,S可能会被匹到尾首相连的一部分。所以,我们可以使用惯用套路——把S复制一份再KMP:
我们可以数出图中的u=10,|a|=2,于是n=u+|a|=12,m=u-|a|=8,w=u*2=20。
那么,可行的串自然是匹配到的绿色部分的前m个字符构成的串(当然,后m个字符也是一样的,随你的便)。前m个字符或后m个字符,任选其一记录,不必重复。比如图中的第一个’aa’起始位置为4(设S的第一个字符的位置为1),那么它在下一半的起始位置即为4+u=14,这两个’aa’相距14-(4+|a|)=u-|a|=m=8,所以恰好一致。
所以说,我们可以从S的第m+1位开始KMP。毕竟从S[4]往前m位,和从S[14]往前m位是一致的。
我们可以计算出每个可行串的哈希值,把它累计到一个桶里面。
当然,如果你暴力去计算哈希值肯定是会TLE的,所以我们可以先计算出S串的哈希值前缀和,然后
计算哈希值。
那么,有木有可能将同一种可行串算重呢?当然有可能。此时,我们只需设一个标记数组,记录每一种可行串的哈希值在当前这个S串中有没有出现过。当然,这样你每次都要清空这个数组,所以你大可将其开为int数组,直接记录最后一次出现过是在第几个S串中的。
那么,对于T串的工作就简单了,我们直接计算出它的哈希值,然后累计桶中的cnt即可。
N<M
形同N>M的做法。只不过我们对于S串,直接计算其哈希值并丢进桶里;对于T串,再将其分割成一个大T串和一个小a串KMP即可。
时间复杂度
时间复杂度: 。
心路历程
我比赛时就想到了,并且还打了,而且自感有点虚,便打了个双哈希。(双哈希求cnt怎么搞?取最小)然而还是惨淡地:
我发现有许多个点我的答案恰好是它的100倍,而且N=M=100。我又发现我的代码(伪代码)是这样打的:
for(;Ts;Ts--)
{
scanf("%s",s+1);
if(n<m){直接计算;continue;}
KMP;
}
for(;Tt;Tt--)
{
scanf("%s",s+1);
if(n>m){直接求答案;continue;}
KMP;
}
这样当n=m的时候,两者都会进行KMP。
当然,我本来就打算让其中一个进行KMP,这样的话将S复制一遍变成SS,a则为空串,所以每个位置都会被匹到,所以每m个字符均为可行串。
但是它把T也进行KMP了,那样T的每个地方都会被统计答案(也即我将S循环,却又将T循环,然后来累计答案)。于是答案就恰好大了M=100倍。
然后我略作修改,又满怀壮志地提交了,结果又:
然后我发现我调试时将数组开小了,忘记开回去了。。。
就此被坑掉100points。。。o(╥﹏╥)o
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define mp make_pair
#define min(a,b) ((a)<(b)?(a):(b))
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int N=4e6+1;
const ll X=13331,M1=999983,M2=1e6+3;
int i,j,Ts,Tt,n,m,w,u,v,b1[M1],b2[M2],h1[M1],h2[M2],len,next[N];
char s[N],a[N];
ll x1,x2,ans;
P cm[N],sum[N];
void pre()
{
cm[0]=mp(1,1);
fo(i,1,w)cm[i]=mp(cm[i-1].fi*X%M1,cm[i-1].se*X%M2);
memset(b1,127,sizeof b1);
memset(b2,127,sizeof b2);
}
void init(int n)
{
int i;
fo(i,1,n)
{
ll x=s[i]-'a';
sum[i]=mp((sum[i-1].fi*X+x)%M1,(sum[i-1].se*X+x)%M2);
}
}
void gethash(int l,int r)
{
x1=(sum[r].fi-sum[l-1].fi*cm[r-l+1].fi%M1+M1)%M1;
x2=(sum[r].se-sum[l-1].se*cm[r-l+1].se%M2+M2)%M2;
}
void KMP(bool t)
{
int j,k=0;
fo(j,2,v)
{
while(k&&a[j]!=a[k+1])k=next[k];
if(a[j]==a[k+1])next[j]=++k;
}
k=0;
fo(j,len+1,w)
{
while(k&&s[j]!=a[k+1])k=next[k];
if(k<v&&s[j]==a[k+1])k++;
if(k==v)
{
int st=j-v+1;
gethash(st-len,st-1);
if(b1[x1]>Ts+Tt||b2[x2]>Ts+Tt)
{
b1[x1]=b2[x2]=Ts+Tt;
if(!t)
h1[x1]++,h2[x2]++;
else ans+=1ll*min(h1[x1],h2[x2]);
}
k=next[k];
}
}
}
int main()
{
scanf("%d%d%d%d",&Ts,&Tt,&n,&m);
u=(w=n+m)>>1;pre();
for(;Ts;Ts--)
{
scanf("%s",s+1);
if(n<=m)
{
init(n);
h1[sum[n].fi]++;
h2[sum[n].se]++;
continue;
}
v=0;
fo(j,u+1,w)
{
if(j<=n)a[++v]=s[j];
s[j]=s[j-u];
}
len=m;
init(w);
KMP(0);
}
for(;Tt;Tt--)
{
scanf("%s",s+1);
if(n>m)
{
init(m);
ans+=1ll*min(h1[sum[m].fi],h2[sum[m].se]);
continue;
}
v=0;
fo(j,1,m-u)a[++v]=s[j];
fo(j,1,u)s[j]=s[j+m-u];
fo(j,j,w)s[j]=s[j-u];
len=n;
init(w);
KMP(1);
}
printf("%lld",ans);
}