小z和Easy的字符串游戏
题目描述
小z玩腻了迷宫游戏,于是他找到了Easy,准备和Easy玩这么一个游戏
小Z准备了一个字符串S (S的长度不超过10000)
又准备了M个小的字符串(M最大不超过1000000,每个小字符串的长度不超过10)
现在小z想请教Easy老师,M个小字符串中有多少个小字符串是大字符串S的子序列?
如果Easy老师答不上来就要请客,现在Easy老师很苦恼,你能帮帮他吗?
子序列可以理解为不要求连续的子串,若还是不了解请看下面的链接中,最佳答案的回答
https://zhidao.baidu.com/question/120638124.html
输入
只有一组测试数据
第一行是大字符串S(S的长度不超过10000)
第二行是一个整数M,表示小字符串的个数(M最大不超过1000000)
接下来M行每行给出一个小字符串(长度不超过10)
保证没有空字符串
输出
输出只有一个整数,代表M个小字符串中是大字符串S的子序列的个数
样例输入
amrocegijgyvkgarnffb 4 i jsj qg ac
样例输出
2
提示
样例解释:
对于4个小字符串
1 i
因为大字符串S里有i,所以i肯定是大字符串,答案+1
2 jsj
大字符串S中没有任何一个子序列是jsj
3 qg
大字符串S中没有任何一个子序列是qg
4 ac
大字符串S中的第1个字符'a'和第5个字符'c'构成的子序列
正好是ac,答案+1
4个小字符串中有2个字符串是大字符串S的子序列
所以答案为 2
思路:可以预处理出一个状态机来,next1[i]['c'] 为母串中i位置之后的c字符出现的位置,这样输入字串之后可以直接在next1数组中跑一边找一下看是否能够跑完字串;
例如 abcdeabcd
next[0]['a'] = 5(即0位置之后的第一个a字符出现在第五个位置);
next[0]['b'] = 6;
……(26个字母都找一边没有的话就是 -1(初值));
next[1][a] = 4(即1位置之后第一个a字符出现在的第4个位置);
……(整个母串的每个位置都找一边26个字母)
之后再用一个数组标记一下 26个字母第一次出现在母串中的位置;
代码 :
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int next1[10005][10005];
int d[10005];
int main()
{
int n;
string s1;
cin >> s1;
memset(next1,-1,sizeof(next1));
memset(d,-1,sizeof(d));
for(int i = 0; i < s1.size(); i++){//标记i位置之后每个字母第一次出现的位置
for(int j = 0; j < 26; j++){
for(int k = i + 1; k < s1.size();k++){
if(s1[k] == 'a' + j){
next1[i][j] = k;
break;
}
}
}
}
for(int i = 0; i < 26; i++){//标记每个字母第一次出现在母串中的位置
for(int j = 0; j < s1.size(); j++){
if(s1[j] == 'a' + i){
d[i] = j;
break;
}
}
}
cin >> n;
string s2;
int ans = 0;
while(n--){
cin >> s2;
int t = d[s2[0] - 'a'];//字串中的第一个字符在母串中第一次的位置
int l = 0;
while(l < s2.size()){
if(t == -1) break;//字串中的字符不存在母串中 赋值为初值-1
else{
l++;
t = next1[t][s2[l] - 'a'];//t代表上一个字母在母串中的位置,在t之后找下一个字母
}
}
if(l >= s2.size()) ans++;
}
printf("%d\n",ans);
}