其实这个东西真的算自动机吗?好像还真的符合自动机的定义啊;
我将在下面用人话来定义序列自动机,并不像某度某科一样不说人话;
设一个字符集S,nxt[i][j]表示第i个位置往后第一个j元素出现的位置;
这个nxt数组可以O(n)的求出来,可以自行验证;
for(int i=n-1;i>=0;--i){ for(int j=1;j<=26;++j) nxt[i][j]=nxt[i+1][j]; nxt[i][s[i+1]-‘a‘+1]=i+1; }
我们会发现一个神奇的事情:这是一个DAG!
她能干什么事情呢?
1.判断是否是原字符串的子序列
当我们构造出nxt数组之后,可以贪心的寻找子序列;
2.求一个序列的子序列个数;(可以限定序列的长度)
我们在DAG上跑拓扑DP,f[v][j]表示从1~v寻则j个元素的方案数;
显然的:f[v][j]+=f[u][j-1];
#include <bits/stdc++.h> #define inc(i,a,b) for(register int i=a;i<=b;i++) #define dec(i,a,b) for(register int i=a;i>=b;i--) using namespace std; char s[3010]; int nxt[3010][40]; int n,m; long long f[3010][3010]; int rudu[3010]; const int p=998244353; queue<int> qwq; void tp() { qwq.push(0); f[0][0]=1; while(qwq.size()){ int u=qwq.front(); qwq.pop(); inc(i,0,25){ if(!nxt[u][i]) continue; inc(j,0,u) f[nxt[u][i]][j+1]=(f[nxt[u][i]][j+1]+f[u][j])%p; --rudu[nxt[u][i]]; if(rudu[nxt[u][i]]==0) qwq.push(nxt[u][i]); } } } int main() { scanf("%s",s+1); n=strlen(s+1);cin>>m; dec(i,n-1,0){ inc(j,0,25) nxt[i][j]=nxt[i+1][j]; nxt[i][s[i+1]-‘a‘]=i+1; inc(j,0,25) if(nxt[i][j]!=0) rudu[nxt[i][j]]++; } tp(); long long ans=0; inc(i,1,n) ans=(ans+f[i][m])%p; cout<<ans%p; } /* addeade 3 aa 1 */
3.求两串的公共子序列个数
两串都构造一下,直接跑就好了
long long dfs(int x,int y){ if(f[x][y]) return f[x][y]; for(int i=1;i<=26;++i) if(nxt1[x][i]&&nxt2[y][i]) f[x][y]+=Dfs(nxt1[x][i],nxt2[y][i]); return ++f[x][y]; }
4.求字符串的回文子序列个数
首先原串与反串都建一遍;
就相当于从左右端点向中间跑自动机;
显然:x+y<=n+1才会合法;
但要注意,我们只能统计偶数长度的字符串,而不能统计奇数个数的字符串;
因为我们永远都是两个两个地串;
long long Dfs(int x,int y){ if(f[x][y]) return f[x][y]; for(int i=1;i<=a;++i) if(nxt1[x][i]&&nxt2[y][i]){ if(nxt1[x][i]+nxt2[y][i]>n+1) continue; if(nxt1[x][i]+nxt2[y][i]<n+1) f[x][y]++; f[x][y]=(f[x][y]+Dfs(nxt1[x][i],nxt2[y][i]))%mod; } return ++f[x][y]; }
DAG
原文地址:https://www.cnblogs.com/kamimxr/p/11823052.html
时间: 2024-10-29 16:20:04