昨天在牛客碰到了这样的一道题,判断一些字符串是不是原串的子序列,,,因为之前做过一些LCS子序列的题,,,就想,这不贼简单,,用lcs求一下每个子串和原串,,然后判断LCS的长度是不是等于要判断的那个串的长度,,,然后,,T了,,,
因为dp求LCS几个串还好说,,但是当串又多又长时,,,不仅会T,,dp数组不弄滚动数组还会MLE,,,
之后看了题解了解到这个处理子序列的好东西,序列自动机,,,
分析
序列自动机实质还是用空间换时间,,它有一个数组 nxt[i][j](nxt[maxn][26]nxt[i][j](nxt[maxn][26],,表示原串s的第i位后面那26个字符j出现的最早的 位置,,
相当于建了一棵树,,根节点是一个空节点,,,它有26个孩子,,表示每一个字母最早出现的位置,,,那么原串的第一个字符 s[0]s[0] 就使得 nxt[0][s[0]−′a′]=1nxt[0][s[0]−′a′]=1,,第二个字符就是 nxt[0][s[1]−′a′]=2nxt[0][s[1]−′a′]=2,,,等等等等,,,同样第一个字符也有这样的26个孩子,,,这样从根节点到任意一个叶子节点都是原串的一个子序列,,
这样判断一个字符串t是不是原串的子序列只要将t中的每一个字符在那棵树里跑一下,,,如果存在这样的路径就表示t是s的一个子序列,,,
那么怎么建树呢,,
如果正着建树的话每次都要找到后面最早出现的字符的位置,,,不太好弄,,所以我们倒着建树,,用一个 now[26]now[26] 数组表示遍历到第i个字符时后面这26个字符从后往前看最晚出现的位置,,也就是第i个字符后面的26个字符最在出现的位置,,,用它来更新 nxt[i][1→26]nxt[i][1→26],,然后再将这个字符在 nownow 数组中的位置更新为当前的位置,,now[s[i]−′a′]=inow[s[i]−′a′]=i,,,
最后的实现就是这样子:
int nxt[maxn][30];
int now[30];
char s[maxn];
void init()
{
//序列自动机预处理
memset(now, -1, sizeof now); //mow_i表示第i个字母在原串中从后向前最晚出现的位置
int len = strlen(s);
--len;
for(int i = len; ~i; --i) //处理每一个字符
{
for(int j = 0; j < 26; ++j) //找出第i个字符后面的26个字母最早出现的字符的位置
nxt[i][j] = now[j];
now[s[i] - 'a'] = i; //用当前字符更新当前字符在原串中从后向前最晚出现的位置
}
}
例题:
链接:https://ac.nowcoder.com/acm/contest/392/J
来源:牛客网
题目描述
月月和华华一起去吃饭了。期间华华有事出去了一会儿,没有带手机。月月出于人类最单纯的好奇心,打开了华华的手机。哇,她看到了一片的QQ推荐好友,似乎华华还没有浏览过。月月顿时醋意大发,出于对好朋友的关心,为了避免华华浪费太多时间和其他网友聊天,她要删掉一些推荐好友。但是为了不让华华发现,产生猜疑,破坏了他们的友情,月月决定只删华华有可能搭讪的推荐好友。
月月熟知华华搭讪的规则。华华想与某个小姐姐搭讪,当且仅当小姐姐的昵称是他的昵称的子序列。为了方便,华华和小姐姐的昵称只由小写字母构成。为了更加方便,保证小姐姐的昵称长度不会比华华的长。
现在月月要快速的判断出哪些推荐好友要删掉,因为华华快回来了,时间紧迫,月月有点手忙脚乱,所以你赶紧写个程序帮帮她吧!
输入描述:
第一行输入一个字符串A表示华华的昵称。
第二行输入一个正整数N表示华华的推荐好友的个数。
接下来N行,每行输入一个字符串BiBi表示某个推荐好友的昵称。
输出描述:
输出N行,对于第i个推荐好友,如果华华可能向她搭讪,输出Yes,否则输出No。
注意大写,同时也要注意输出效率对算法效率的影响。
代码:
#include<bits/stdc++.h>
using namespace std;
int nxt[1000006][27],now[27],n;
string s,t;
int main(){
fill(now,now+27,-1);
cin>>s;
int len=s.size();
for(int i=len-1;i>=0;i--){
for(int j=0;j<26;j++)
nxt[i][j]=now[j];
now[s[i]-'a']=i;
}//序列自动机初始化
cin>>n;
while(n--){
cin>>t;
bool flag=true;
len=t.size();
int lac=now[t[0]-'a'];
if(lac==-1){printf("No\n");}
else {
for(int i=1;i<len;i++){
lac=nxt[lac][t[i]-'a'];
if(lac==-1){
flag=false;
break;
}
}
if(flag)printf("Yes\n");
else printf("No\n");
}
}
return 0;
}