序列自动机
是看了LZL大佬的博客才学到的一种新查找方式,感觉还挺好用的.就记录下来
作用是:在一个文本串中查找子序列是否存在或者组成字符的最近位置
这就是不能用kmp算法的地方, 因为kmp算法适用于 模式串是由文本串中连续的若干字符组成 的查找
序列自动机里用到了next二维数组, next[][],储存着在i位置之后最近的j字符的位置在哪儿.
例如next[5][2] = x: 首先2是由 'c' - 'a'得到的,所以代表着c字符在5位置之后的最近位置x
需要注意的一点是文本串需要预留出一个0位置的空间出来作为起点,所以输入变为
scanf("%s", str + 1);//这样就预留出了0位置
获得next数组的操作以及注解
void getnext() { memset(next, 0, sizeof(next));//初始化为0代表i位置之后没有该字符 int len = strlen(str + 1);//长度相应的从1下标开始 for(int i = len; i >= 1; i --) { for(int j = 0; j < 26; j ++) { next[i - 1][j] = next[i][j];//str i-1位置继承str i位置的离其它字符最近的位置是第几个 } next[i - 1][str[i] - 'a'] = i;// str i-1位置离str[i]字符的最近位置变为第i个. } }
查找部分代码
int flag = 1; scanf("%s", s); int len = strlen(s);//模式串长度 int now = 0;//起点0记录了所有字符的最近位置,从0开始找 for(int i = 0; i < len; i ++) { now = next[now][s[i] - 'a']; if(!now)//如果查到某个字符结果为0说明now位置之后不再有该字符,标记跳出 { flag = 0; break; } } if(flag) printf("Yes\n"); else printf("No\n");
例题在此:https://ac.nowcoder.com/acm/contest/392/J
AC完整代码
#include<stdio.h> #include<string.h> char str[1000000 + 10]; char s[1000000 + 10]; int next[1000000 + 10][30]; void getnext() { memset(next, 0, sizeof(next));//初始化为0代表i位置之后没有该字符 int len = strlen(str + 1);//长度相应的从1下标开始 for(int i = len; i >= 1; i --) { for(int j = 0; j < 26; j ++) { next[i - 1][j] = next[i][j];//str i-1位置继承str i位置的离其它字符最近的位置是第几个 } next[i - 1][str[i] - 'a'] = i;// str i-1位置离str[i]字符的最近位置变为第i个. } } int main() { scanf("%s", str + 1); getnext(); //获得序列自动机的next数组 int T; scanf("%d", &T); getchar(); while(T --) { int flag = 1; scanf("%s", s); int len = strlen(s);//模式串长度 int now = 0;//起点0记录了所有字符的最近位置,从0开始找 for(int i = 0; i < len; i ++) { now = next[now][s[i] - 'a']; if(!now)//如果查到某个字符结果为0说明now位置之后不再有该字符,标记跳出 { flag = 0; break; } } if(flag) printf("Yes\n"); else printf("No\n"); } return 0; }