题意就是,给出一个字符串S,接着会有n组查询,每组查询会有一个字符串s2,问你这个字符串是不是包含在S中。
这里的包含指的是,顺序不改变就行,不一定要紧挨着,一开始我看错了题目,直接上kmp,写了一半发现不行。
这题如果我们暴力去找,我们会发现,中间很多过程都是浪费的,比如S="acccccccccccccccccccccccccca",
如果我们要去判断s2="aa" 这个字符串在不在S中,中间这么多c都是无效比较。
那么其实如果能一步知道下一个字符的位置,那该多好,类似于kmp算法的next指针跳跃。这是一个质的飞跃压。
所以我们就可以预处理出一个二维数组 next[i][j] 表示第i个位置后面,字母a-z出现的位置,如果出现很多个,我们只记录最前面的,只记录最前面的我们能保证不会遗漏其他的。i最大是1e6,j最大就26,因为26个字母嘛
处理的时候,我们应该从后往前处理。
处理好这个二维next数组后,那么接下来直接for循环跑一边我们要查询的字符串就OK了
本弱刚开始因为在预处理的时候,字符串下标从0开始,一直有个小bug,改1开始就好了
下面给出1个小测试,供大家参考
aaaa
2
aaaa
aaaaa
Yes
No
代码有详细注释:
#include <bits/stdc++.h> using namespace std; const int N=1e6+100;//maxlen int q;//q组查询 char s[N];//模式串 char p[N];//匹配串 int _next[N][26]; //表示第i个位置后面'a'-'z'的 int last[26]; //用来存当前 'a'-'z'的位置 int main() { scanf("%s",s+1); int len=strlen(s+1); for(int i=len-1;i>=0;i--) {//我们从倒数第二位开始处理 last[s[i+1]-'a']=i+1;//由于我们是从倒数第二位开始的,首先把最后一位的位置算好 //算好之后,再copy给 _next[i][j] for(int j=0;j<26;j++) { _next[i][j]=last[j];//第i为开始的后面26个字母是等于是他后面一位的26个字母的位置 } } scanf("%d",&q); while(q--) { scanf("%s",p+1); int len2=strlen(p+1); if(len2>len) {//如果比模式串都要长,不可能存在子串 printf("No\n"); continue; } bool f=false; int pos=0; for(int i=1;i<=len2;i++) { if(_next[pos][p[i]-'a']) { pos=_next[pos][p[i]-'a']; } else { f=true; break; } } if(!f)printf("Yes\n"); else printf("No\n"); } return 0; }