序列自动机入门

Prob1

给你一个长度为1000000字符串s
然后给你1000000个问题
问a是不是s的子序列

Sol:

序列自动机是用来判断是否是子序列的算法 时间复杂度是 O(len)

nx[i][j] 数组存的是在 s 中第 i 位后面第一个 j 字母出现的位置

Prog:

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'
';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 1e9 + 7;
const int maxn = 1e6 + 10;
int n, t, nxt[maxn][30];
char s[maxn], str[maxn];

int main() {
	scanf("%s", s+1);
	int len = strlen(s+1);
	for(int i=len; i; i--) //逆循环 
	{
		for(int j=0; j<26; j++) //26个字母 
		    nxt[i-1][j] = nxt[i][j];
		nxt[i-1][s[i]-'a'] = i;
	}
	int a,b;
/*	while (true)
	{
		cin>>a>>b;
		cout<<nxt[a][b]<<endl;
		
	 } 
*/
	scanf("%d", &t);
	while(t--)
	{
		scanf("%s", str);
		int lenc = strlen(str), f = 0;
		for(int i=0, now=0; i<lenc; i++)
		{
			now = nxt[now][str[i]-'a'];
			if(!now) 
			{
				f = 1;
				break;
			}
		}
		if(f) puts("No");
		else puts("Yes");
	}
}

  

Prob2:给出一个字符串统计其本质不同的子序列个数

SOL:记忆化搜索

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'
';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 1e9 + 7;
const int maxn = 3e3 + 10;
int n, nxt[maxn][30], f[maxn];
char s[105];

int dfs(int x) {
	if(f[x]) 
	   return f[x];
	for(int i=0; i<26; i++)
		if(nxt[x][i]) f[x] += dfs(nxt[x][i]);
	return ++f[x];
}

int main() {
	scanf("%d%s", &n, s+1);
	for(int i=n; i; i--) {
		for(int j=0; j<26; j++) nxt[i-1][j] = nxt[i][j];
		nxt[i-1][s[i]-'a'] = i;
	}
	int num = dfs(0);
	printf("%d
", num);
}

另一个做法:

zz https://blog.csdn.net/weixin_35338624/java/article/details/88571242

https://blog.csdn.net/oranges_c/article/details/53364269?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1

Leetcode 940:不同的子序列II

题目描述
给定一个字符串 S,计算 S 的不同非空子序列的个数。
因为结果可能很大,所以返回答案模 10^9 + 7.

示例 1:

输入:"abc"

输出:7
解释:7 个不同的子序列分别是 "a", "b", "c", "ab", "ac", "bc", 以及 "abc"。


示例 2:

输入:"aba"
输出:6
解释:6 个不同的子序列分别是 "a", "b", "ab", "ba", "aa" 以及 "aba"。


示例 3:

输入:"aaa"
输出:3
解释:3 个不同的子序列分别是 "a", "aa" 以及 "aaa"。

提示:

S 只包含小写字母。
1 <= S.length <= 2000
解题思路
动态规划,dp[i]定义为以i位置字母结尾的不同子序列个数,当没个字母唯一出现一次时,状态转换方程为dp[i+1] = 2*dp[i],但是根据题目示例,会出现重复的子序列,于是用last[26]记录每个S中的字母最后一次出现的位置,当某一个字母至少出现一次时使用dp[i+1]减去相应的数目就行

int distinctSubseqII(string S) {
        int len=S.length();
        int mod = 1e9+7;
        int dp[len+1] = {0};
        int last[26];
        memset(last,-1,sizeof(last));
        dp[0]=1;
        for(int i=0;i<len;i++){
            dp[i+1]=(2*dp[i])%mod;
            int idx=S[i]-'a';
            if(last[idx]>=0){
                dp[i+1]=(dp[i+1]-dp[last[idx]] + mod)%mod;
            }
            last[idx]=i;
        }
        dp[len] = (dp[len]-1)%mod;
        return dp[len];
    }

  当然此题还可以用后缀数组,后缀自动机等方法来完成 .

可参考:https://blog.csdn.net/weixin_41863129/article/details/95060033

原文地址:https://www.cnblogs.com/cutemush/p/12627545.html