【COCI2012】覆盖字符串

【题目描述】

给出一个长度为N的小写字母串,现在Mirko有M个若干长度为Li字符串。现在Mirko要用这M个字符串去覆盖给出的那个字符串的。覆盖时,必须保证:
1.Mirko的字符串不能拆开,旋转;
2.Mirko的字符串必须和给出的字符串的某一连续段完全一致才能覆盖,
3.若干次覆盖可以部分重叠
4.Mirko的字符串可以无限使用。
求给出的字符串当中,有多少个字母是无法覆盖的。

【题解】

很魔性的一道题,一看就知道是AC自动机,这里有个小优化,插入时判断一下:如果模式串是文本的子串就插入。

然后就转化为了线段覆盖问题。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<ctime>
 7 #include<algorithm>
 8 using namespace std;
 9 #define MAXN 2500010
10 int n,m,cnt,ans,v[MAXN],q[MAXN],fail[MAXN],k[MAXN],tr[1000100][27],vis[27][27][27][27][27];
11 char b[MAXN],ch[5010];
12 inline int read()
13 {
14     int x=0,f=1;  char ch=getchar();
15     while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
16     while(isdigit(ch))  {x=x*10+ch-'0';  ch=getchar();}
17     return x*f;
18 }
19 void insert(int p)
20 {
21     int now=0;
22     for(int i=1;i<=p;i++)
23     {
24         if(!tr[now][ch[i]]) tr[now][ch[i]]=++cnt;
25         now=tr[now][ch[i]];
26     }
27     v[now]=p;
28 }
29 void build()
30 {
31     int head=0,tail=0;
32     for(int i=0;i<26;i++)  if(tr[0][i])  q[++tail]=tr[0][i];
33     while(++head<=tail)
34     {
35         int x=q[head];
36         for(int i=0;i<26;i++)
37         {
38             int y=tr[x][i];
39             if(!y) continue;
40             q[++tail]=y;
41             int temp=fail[x];
42             while(temp&&!tr[temp][i])  temp=fail[temp];
43             fail[y]=tr[temp][i];
44         }
45     }
46 }
47 void work()
48 {
49     for(int i=1,x=0;i<=n;i++)
50     {
51         while(x&&!tr[x][b[i]]) x=fail[x]; x=tr[x][b[i]];  int temp=x;
52         while(temp) {if(v[temp]) k[i-v[temp]+1]=v[temp];  temp=fail[temp];}
53     }
54     for(int i=1,last=0;i<=n;i++)
55     {
56         if(k[i])  last=max(last,k[i]+i-1);
57         if(last<i)  ans++;
58     }
59     printf("%d
",ans);
60 }
61 int main()
62 {
63     //freopen("cin.in","r",stdin);
64     //freopen("cout.out","w",stdout);
65     n=read();  scanf("%s",b+1);
66     for(int i=1;i<=n;i++)  b[i]=b[i]-'a';
67     for(int i=5;i<=n;i++)  vis[b[i]][b[i-1]][b[i-2]][b[i-3]][b[i-4]]=1;
68     m=read();
69     for(int i=1;i<=m;i++)  
70     {
71         scanf("%s",ch+1);  bool flag=0;  int len=strlen(ch+1);
72         for(int j=1;j<=len;j++)  ch[j]=ch[j]-'a';
73         for(int j=5;j<=len;j++)  if(!vis[ch[j]][ch[j-1]][ch[j-2]][ch[j-3]][ch[j-4]]) {flag=1;break;}
74         if(!flag)  insert(len);
75     }
76     build();
77     work();
78     return 0;
79 }