The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e'. He was a member of the Oulipo group. A quote from the book:
Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…
Perec would probably have scored high (or rather, low) in the following contest. People are asked to write a perhaps even meaningful text on some subject with as few occurrences of a given “word” as possible. Our task is to provide the jury with a program that counts these occurrences, in order to obtain a ranking of the competitors. These competitors often write very long texts with nonsense meaning; a sequence of 500,000 consecutive 'T's is not unusual. And they never use spaces.
So we want to quickly find out how often a word, i.e., a given string, occurs in a text. More formally: given the alphabet {'A', 'B', 'C', …, 'Z'} and two finite strings over that alphabet, a word W and a text T, count the number of occurrences of W in T. All the consecutive characters of W must exactly match consecutive characters of T. Occurrences may overlap.
题意:给出了两个字符串,问第一个串在第二个中可以重复地出现过多少次。
我一开始是用AC自动机做了一次,建了模式串的AC自动机后,待匹配串每次走到词尾节点ans++。
后来又用KMP做了一次,先模式串自匹配,再与待匹配串匹配的时候记录ans
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 // 有nxt数组版本。。该程序不能判别相同模式串,因此若模式串重复,答案会将相同模式串当做不同的处理,因此若需要可以用map去重或修改insert
2 #include<stdio.h>
3 #include<string.h>
4 #include<queue>
5 using namespace std;
6 const int maxm=5000006; //maxm是总结点数:约为字母数+++
7
8 char s[1000005],word[10005];
9 int nxt[maxm][26],tail[maxm],f[maxm],size; //nxt是结点指向不同字母的结点下标,tail是表示该结点为几个单词的词尾(可能需要计算重复的模式串情况),f是当不匹配时转跳到的结点下标,size是结点数
10 int last[maxm]; //last指针是指向上一个是单词结尾的结点,由于是由失配指针拓展得到的,因此所指向的单词都是该结点表示的单词的后缀单词,但由于可能卡空间,所以虽然可以在时间上优化,但是有时并不使用
11
12 int newnode(){ //初始化整个trie或建立新的结点时,首先初始化当前结点所指向的26个字母的结点为0,表示暂时还没有指向的字母,然后暂定该结点不是单词尾结点,暂无失配时转跳位置(即转跳到根节点),返回结点标号
13 memset(nxt[size],0,sizeof(nxt[size]));
14 f[size]=tail[size]=0;
15 return size++;
16 }
17
18 void insert(char s[]){ //构造trie,p为当前结点的上一个结点标号,初始为0;x即为当前结点(上个结点标号指向当前字母的结点)标号,若此结点还未出现过,那么就建立这个结点;然后更新p为当前结点标号以便后续操作
19 int i,p=0;
20 for(i=0;s[i];i++){
21 int &x=nxt[p][s[i]-'A'];
22 p=x?x:x=newnode();
23 }
24 tail[p]++; //此时仅将s串记录,即将s串结尾的结点加1,若无相同模式串,则此操作只会使所有串尾结点的tail值由0变为1,但有相同模式串,则会重复记录,需要去重可以用map或用tail[p]=1;语句来完成
25 }
26
27 void makenxt(){ //利用bfs来构造失配指针
28 int i;
29 queue<int>q;
30 f[0]=0;
31 for(i=0;i<26;i++){ //首先将0结点(根节点)连接的字母结点加入队列,并定失配指针和last指针都指向0结点
32 int v=nxt[0][i];
33 if(v){
34 f[v]=last[v]=0;
35 q.push(v);
36 }
37 }
38 while(!q.empty()){
39 int u=q.front();
40 q.pop();
41 for(i=0;i<26;i++){
42 int v=nxt[u][i];
43 if(!v)nxt[u][i]=nxt[f[u]][i]; //当u结点没有i对应字母,则视为失配,将其指向失配后转跳到的结点所指向的i对应字母
44 else{
45 q.push(v); //u结点存在指向i的结点,则将所指向的结点下标加入队列
46 f[v]=nxt[f[u]][i]; //设置这个结点的失配指针指向上个结点失配后的指向字母i的结点,由于bfs一定会从字典树浅层到深层,即从字符串短到长,而失配转跳后表示的字符串长度严格减少,所以只需要指向一次即可
47 last[v]=tail[f[v]]?f[v]:last[f[v]]; //若失配指针指向的结点是单词结尾,那么当前结点失配后就可以直接指向失配结点,即失配路径上的上一个单词结点,若失配结点不是单词结尾,就指向失配结点的last
48 }
49 }
50 }
51 }
52
53 int query(char s[]){ //查询s串中模式串出现了多少种/次
54 int ans=0,v=0;
55 for(int i=0;s[i];i++){
56 while(v&&!nxt[v][s[i]-'A'])v=f[v];
57 v=nxt[v][s[i]-'A'];
58 int tmp=v;
59 while(tmp){
60 ans+=tail[tmp];
61 tmp=last[tmp];
62 }
63 }
64 return ans;
65 }
66
67 int main(){
68 int T;
69 scanf("%d",&T);
70 while(T--){
71 size=0,newnode();
72 scanf("%s",word);
73 insert(word);
74 makenxt();
75 scanf("%s",s);
76 printf("%d\n",query(s));
77 }
78 return 0;
79 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<stdio.h>
2 #include<string.h>
3
4 char s[1000005],t[10005]; //s为待匹配串,t为模板串
5 int p[10005]; //自匹配数组
6
7 int main(){
8 int T;
9 scanf("%d",&T);
10 while(T--){
11 scanf("%s%s",t,s);
12 int n=strlen(s);
13 int m=strlen(t);
14 int i,j,ans=0;
15 p[0]=p[1]=0;
16 for(i=1;i<m;i++){
17 j=p[i];
18 while(j&&t[i]!=t[j])j=p[j];
19 p[i+1]=t[i]==t[j]?j+1:0;
20 }
21 j=0;
22 for(i=0;i<n;i++){
23 while(j&&s[i]!=t[j])j=p[j];
24 if(s[i]==t[j])j++;
25 if(j==m)ans++;
26 }
27 printf("%d\n",ans);
28 }
29 return 0;
30 }