转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
题意 :给出一个A串,给出若干B串,问A中有多少个不相同的子串是以某个B串为后缀的。
Dshawn找我出个SAM,为了nanae的生日赛。
无聊的跨专业课,就出了这个个题吧~~~~~
对于SAM来说,解法比较简单。
首先将A串和B串都反向之后,后缀便为前缀,题目转变为A串中有多少个不相同的子串是以某个B串为前缀的。
那么以A串建立SAM之后,首先预处理一下,对于A中的每一个子串,存在多少个不同的后继。这就是一个简单的DAG上的DP。得到的结果其实就是,以当前子串为前缀的不同的子串有多少个 (= =这样说的话,问题就解决了。
遍历每一个B串,如果A中存在这个子串,那么就统计一下这个子串有多少个后继,叠加一下就行了。
这样明显有个问题,便是存在重复子串。
大概 就是如果某个结点被统计过,那么他后继中的所有结点的后继,都已经被包括其中。
那么其中一种处理方法便是,处理完之后,把可达的结点标记一下,然后通过DAG关系,依次处理,而且将所有后继中的标记撤消。
那么另外一种处理方法便是,问题的源由在于,B串中某些串是某个串的前缀之类的。那么将B按字典序排序之后,依次处理,如果某个B经过了已经被标记过的结点,那么直接退出。
AekdyCoin的做法是SA,巨巨真可怕,不过不得不说SA的做法好麻烦,耗时也多。ORZ
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define LL long long
using namespace std;
const int N=210005;
struct Node{
char word[105];
bool operator<(const Node n)const{
return strcmp(word,n.word)<0;
}
}a[1005];
struct SAM {
SAM *pre,*son[26];
int f,len;
LL cnt;
}*root,*tail,que[N],*b[N];
char str[N];
int cnt[N];
int tot=0;
int c[N];
int n;
void add(int c,int l){
SAM *p=tail,*np=&que[tot++];
np->len=l;
while(p&&p->son[c]==NULL) p->son[c]=np,p=p->pre;
if(p==NULL) np->pre=root;
else{
SAM *q=p->son[c];
if(p->len+1==q->len) np->pre=q;
else{
SAM *nq=&que[tot++];
*nq=*q;
nq->len=p->len+1;
np->pre=q->pre=nq;
while(p&&p->son[c]==q) p->son[c]=nq,p=p->pre;
}
}
tail=np;
}
LL ans=0;
int main(){
// freopen("1.in","r",stdin);
// freopen("output.txt","w",stdout);
root=tail=&que[tot++];
scanf("%s",str);
int l=strlen(str);
for(int i=0;i<l/2;i++) swap(str[i],str[l-1-i]);
for(int i=0;str[i];i++) add(str[i]-'a',i+1);
for(int i=0;i<tot;i++) c[que[i].len]++;
for(int i=1;i<tot;i++) c[i]+=c[i-1];
for(int i=0;i<tot;i++) b[--c[que[i].len]]=&que[i];
for(int i=0;i<tot;i++) que[i].cnt=1;
for(int i=tot-1;i>=0;i--){
SAM *p=b[i];
for(int j=0;j<26;j++){
if(p->son[j])
p->cnt+=p->son[j]->cnt;
}
}
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%s",a[i].word);
int l=strlen(a[i].word);
for(int j=0;j<l/2;j++)
swap(a[i].word[j],a[i].word[l-1-j]);
}
sort(a,a+n);
for(int j=0;j<n;j++){
SAM *p=root;
bool flag=true;
for(int i=0;a[j].word[i]&&flag;i++){
int s=a[j].word[i]-'a';
if(p->son[s]==NULL) flag=false;
else{
if(p->son[s]->f) flag=false;
p=p->son[s];
}
}
if(flag){
ans+=p->cnt;
p->f=1;
}
}
printf("%lld\n",ans);
return 0;
}