【Luogu】P2536病毒检测(Trie上DP)
这道题我写了个01DP,f[i][j]表示跑到Trie上第i个节点,匹配到字符串第j位行不行
然后重点在*号无限匹配怎么处理
经过一番脑洞我们可以发现*号无限匹配可以拆成两种情况:
1:匹配数无条件+1,但是字符串仍然匹配到底j位
2:直接跳过去了qwq
对应设计状态转移方程就好了
然后这个算法……我卡时卡空间勉强过
卡时不知道怎么办……也许能循环展开什么的?
卡空间的话,可以设滚动数组。
f[i][j]中的i不再代表Trie上第i个节点,而是代表深度第i层的节点
DP的时候用dfs的方法DP,DP完了统计一下就可以把原来叶子占用的空间清掉腾给别人
Orz rqy,这真是个神办法
// luogu-judger-enable-o2 #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cctype> #include<queue> #define maxn 505 #define maxl 1020 using namespace std; int tree[maxn*maxn][10],tot; int fail[maxn*maxn]; char s[maxl]; char c[maxl]; int pnt[maxn]; bool f[200000][maxn]; inline int count(char i){ if(i=='A') return 0; if(i=='T') return 1; if(i=='C') return 2; if(i=='G') return 3; if(i=='*') return 4; return 5; } void update(int x){ int n=strlen(c+1),now=0; for(int i=1;i<=n;++i){ if(tree[now][count(c[i])]==0) tree[now][count(c[i])]=++tot; now=tree[now][count(c[i])]; } pnt[x]=now; return; } void makefail(){ queue<int>q; for(int i=0;i<6;++i) if(tree[0][i]) q.push(tree[0][i]); while(!q.empty()){ int from=q.front(); q.pop(); for(int i=0;i<6;++i){ int &now=tree[from][i]; if(now==0){ now=tree[fail[from]][i]; continue; } fail[now]=tree[fail[from]][i]; q.push(now); } } return; } int main(){ scanf("%s",s+1);int len=strlen(s+1); int n; scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%s",c+1); update(i); } //makefail(); f[0][0]=1; for(int i=0;i<=tot;++i){ for(register int j=0;j<len;++j){ if(s[j+1]=='A') if(tree[i][0]) f[tree[i][0]][j+1]|=f[i][j]; if(s[j+1]=='T') if(tree[i][1]) f[tree[i][1]][j+1]|=f[i][j]; if(s[j+1]=='C') if(tree[i][2]) f[tree[i][2]][j+1]|=f[i][j]; if(s[j+1]=='G') if(tree[i][3]) f[tree[i][3]][j+1]|=f[i][j]; if(s[j+1]=='?') for(int k=0;k<5;++k) if(tree[i][k]) f[tree[i][k]][j+1]|=f[i][j]; if(s[j+1]=='*'){ f[i][j+1]|=f[i][j]; for(int k=0;k<5;++k) if(tree[i][k]) f[tree[i][k]][j]|=f[i][j]; } } for(int j=0;j<5;++j) f[tree[i][j]][len]|=f[i][len]; } int ans=n; for(int i=1;i<=n;++i) if(f[pnt[i]][len]==1) ans--; printf("%d\n",ans); return 0; } /* A*G?C 3 AGTC AGTGTC AGTGC */