这个题就是先给子串,然后给母串,问子串在母串中出现的次数。
Oulipo
Time Limit: 1000 ms / Memory Limit: 32768 kb
Description
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.
Input
The first line of the input file contains a single number: the number of test cases to follow. Each test case has the following format:
One line with the word W, a string over {'A', 'B', 'C', …, 'Z'}, with 1 ≤ |W| ≤ 10,000 (here |W| denotes the length of the string W).
One line with the text T, a string over {'A', 'B', 'C', …, 'Z'}, with |W| ≤ |T| ≤ 1,000,000.
Output
For every test case in the input file, the output should contain a single number, on a single line: the number of occurrences of the word W in the text T.
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
1
3
0
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10000005;
int knext[maxn];
char s[maxn],t[maxn];
int slen,tlen;
void getnext()
{
int j,k;
j=0;k=-1;knext[0]=-1;
while(j<tlen)
{
if(k==-1||t[j]==t[k])
{
knext[++j]=++k;
}
else
k=knext[k];
}
}
int count_kmp()
{
int i=0,k=0;
int ans=0;
int j=0;
if(slen==1&&tlen==1)
{
if(s[0]==t[0])
return 1;
else
return 0;
}
getnext();
for(i=0;i<slen;++i)
{
while(j>0&&s[i]!=t[j])
j=knext[j];
if(s[i]==t[j])
j++;
if(j==tlen)
{
ans++;
j=knext[j];
}
}
return ans;
}
int main(void)
{
int a;
scanf("%d",&a);
while(a--)
{
scanf("%s",t);
scanf("%s",s);
slen=strlen(s);
tlen=strlen(t);
printf("%d\n",count_kmp());
}
}
解释一下,next数组相当于在子串中找到相同的部分,使得已经匹配过的部分不需要再一次匹配。
比如说子串adsccadc 母串adsccadsccadc
当匹配到
adsccadsccadc
adsccadc
这时候c与s不相同 按照朴素算法应该是
adsccadsccadc
adsccadc
这样再进行匹配
但是我们可以看到 子串中的最后三位adc的ad与母串中的ad已经匹配了 又因为子串一开始就是ad开头的所以无需再进行ad的匹配,直接
adsccadsccadc
adsccadc
这里的移动必须是后缀和前缀相同的(后缀就是从中间的某一位到最后的子串,前缀就是从第一位到最后一位之前的某一位的子串),比如子串中前缀ad(第一个ad)与后缀中ad(第二个ad)是相同的。但是如果后缀往前加一位比如cad这时候如果cad匹配,那么ad是显然不匹配的
adsccadsccadc
adsccadc
所以next数组就是寻找相同的前缀与后缀
这里的next数组如果前缀第一位和后缀第一位相同或者不同,next【k】都是0
但是
前缀第一位和后缀第一位相同那么next[k]=0;
这时候再看第二位,如果和前缀第二位相同,那么在之前的字符的next[k]的大小上+1
比如从子串第二个ad开始的后缀 a与a相同 next[5]=0 再往下看 d与d相同 那么next[6]=next[5]+1
再往下看s与c不同 那么next[7]=0
这个意思就是说第二个adc的c的next是2 就是说前面的都匹配了 但是c开始不匹配了 就转到子串下标为2的地方继续匹配。
子串adsccadcada的next数组为
-1,0,0,0,0,0,1,2,0,1,2