传送门:https://www.acwing.com/problem/content/162/(acwing有视频讲解,题解,数据之类的)

题意:给你两个字符串a和b,有q次询问,每次询问输出a的所有后缀和b恰好匹配长度为x的后缀个数。

题解:这个题好微妙啊,我换了两种思路都不太对。然后看了一下大雪菜老师的视频和(传送门)这个题解,豁然开朗。

先求出 b 串的 next 数组,然后a和b串进行匹配。a 串遍历用 i,b串用 j,j 正好是b串能匹配以 i 为结尾的a串的长度,所以就是以 i-j+1 为头的串。根据 kmp 中 next 数组的性质以 i-next[j]+1 的串和 b 恰好匹配的长度也为 j,然后依次套娃...直到 next[j]==-1 结束,用 res[j] 记录。但是这样套娃时间复杂度太大了O(nm)(菜鸡不会算嘤嘤嘤,借助大佬的时间复杂度),其实这就是一个拓扑的结构,只要最后记录完之后在算就会节省很多时间。res[x] 记录的是匹配的前缀至少为 x 的后缀数量,但我们要求的是恰好为x的数量,所以减去 res[x+1] 就是答案了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int nt[200100];
 5 int res[200100];
 6 map<int,int>p;
 7 
 8 void getnext(string p)
 9 {
10     nt[0]=-1;
11     int i=0,j=-1;
12     int len=p.size();
13     while(i<len){
14         if(j==-1||p[i]==p[j]){
15             i++,j++;
16             nt[i]=j;
17         }
18         else j=nt[j];
19     }
20 }
21 
22 void kmp(string t,string p)
23 {
24     getnext(p);
25     int i=0,j=0;
26     int lt=t.size(),lp=p.size();
27     while(i<lt){
28         if(j==-1||t[i]==p[j]){
29             i++,j++;
30             res[j]++;
31         }
32         else j=nt[j];
33     }
34 }
35 
36 int main()
37 {
38     ios::sync_with_stdio(false);
39     cin.tie(0);
40     cout.tie(0);
41     int n,m,t;
42     cin>>n>>m>>t;
43     string a,b;
44     cin>>a>>b;
45     kmp(a,b);
46     for(int i=n;i>=1;i--) res[nt[i]]+=res[i];
47     while(t--){
48         int x;
49         cin>>x;
50         cout<<res[x]-res[x+1]<<endl;
51     }
52     return 0;
53 }