题目链接:http://codeforces.com/contest/235/problem/C
题意:给出两个串S和T,问S有多少个长度为len(T)的子串s,使得s截成两半交换位置后得到的串是T?
思路:将S建立自动机,将两个T接在一起得到T'。在自动机上跑T'。自动机的每个节点记录这个串出现的次数。
const int KIND=26;
struct SAM
{
SAM *son[KIND],*pre;
int len,cnt,flag;
};
SAM sam[N],*head,*last,*b[N];
int d[N],cnt,f[N],len;
char s[N];
void initSAM()
{
head=last=&sam[0];
cnt=1;
}
void insert(int x)
{
SAM *p=&sam[cnt++],*u=last;
p->len=last->len+1;
last=p;
for(;u&&!u->son[x];u=u->pre) u->son[x]=p;
if(!u) p->pre=head;
else if(u->son[x]->len==u->len+1) p->pre=u->son[x];
else
{
SAM *r=&sam[cnt++],*q=u->son[x];
*r=*q; r->len=u->len+1;
p->pre=q->pre=r;
for(;u&&u->son[x]==q;u=u->pre) u->son[x]=r;
}
}
int main()
{
initSAM(); RD(s); len=strlen(s);
int i,x;
FOR0(i,len) insert(s[i]-'a');
FOR0(i,cnt) d[sam[i].len]++;
FOR1(i,len) d[i]+=d[i-1];
FOR0(i,cnt) b[--d[sam[i].len]]=&sam[i];
SAM *p=head;
FOR0(i,len)
{
x=s[i]-'a';
p=p->son[x];
p->cnt=1;
}
FORL0(i,cnt-1)
{
p=b[i];
if(p->pre) p->pre->cnt+=p->cnt;
}
int L,Q,ans,flag=0;
RD(Q);
while(Q--)
{
flag++;
scanf("%s",s);len=strlen(s);
p=head; L=0; ans=0;
FOR0(i,len+len)
{
if(i>=len) x=s[i-len]-'a';
else x=s[i]-'a';
if(p->son[x]) L++,p=p->son[x];
else
{
while(p&&!p->son[x]) p=p->pre;
if(!p) L=0,p=head;
else L=p->len+1,p=p->son[x];
}
if(L>=len)
{
while(p->pre&&p->pre->len>=len)
//这个地方为什么要向前找呢
//因为若p->pre->len>=len,则说明p所在节点向前len的串和
//p->pre节点向前len的串是一样的
//从自动机的性质上来看,p节点和p->pre节点是有p->pre->len的公共前缀的
{
p=p->pre;
L=p->len;
}
if(flag!=p->flag) p->flag=flag,ans+=p->cnt;
}
}
PR(ans);
}
return 0;
}
本文解析了CodeForces竞赛中一道题目,该题要求找出字符串S中所有长度等于T的子串,当这些子串的两部分互换后能形成T。通过构建自动机并运行双倍T得到的T',记录每个状态的出现次数,巧妙地解决了问题。
874

被折叠的 条评论
为什么被折叠?



