序列自动机入门
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)<<'\n'; 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)<<'\n'; 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\n", 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