第一次做Trie上dp,感谢 @i207M 的资瓷
对子串们建立一棵Trie,设$dp[i][j]$表示到母串第$i$位为止在$Trie$上的$j$号节点时的最小修改数量,然后就可以枚举母串各位与Trie的节点dp了。
首先有两个显然的转移
$dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1)$(将当前字符认为是噪音,继续考虑下一位)
$dp[i+1][son[s[i+1]]]=min(dp[i+1][son[s[i+1]]],dp[i][j])$(在Trie上匹配一个)
然后发现并不对......
事实上还有一个转移,当我们匹配完一个子串时,我们又回到了Trie的根节点,即对于每个结束节点$j$有
$dp[i][0]=min(dp[i][0],dp[i][j])$
好像凉了,转移回根节点就出环了。不过事实上只需要单独把根节点拿出来最后转移即可,就是在每一位转移结束后讨论一下根节点的情况
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=305,M=605,L=26; 6 int trie[M*L][27],las[M],dp[N][M*L]; 7 char talk[N],word[M][L]; 8 int n,m,tot,ans=1e9; 9 bool ed[M*L]; 10 void Insert(char *s,int id) 11 { 12 int len=strlen(s),nde=0; 13 for(int i=0;i<len;i++) 14 { 15 int val=s[i]-'a'+1; 16 if(!trie[nde][val]) 17 trie[nde][val]=++tot; 18 nde=trie[nde][val]; 19 } 20 las[id]=nde,ed[nde]=true; 21 } 22 int main () 23 { 24 scanf("%d%d%s",&n,&m,talk+1); 25 for(int i=1;i<=m;i++) 26 talk[i]=talk[i]-'a'+1; 27 for(int i=1;i<=n;i++) 28 scanf("%s",word[i]),Insert(word[i],i); 29 memset(dp,0x3f,sizeof dp),dp[0][0]=0; 30 for(int i=0;i<=m;i++) 31 { 32 for(int j=1;j<=tot;j++) 33 { 34 dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1); 35 if(ed[j]) dp[i][0]=min(dp[i][0],dp[i][j]); 36 int nde=trie[j][(int)talk[i+1]]; 37 if(nde) dp[i+1][nde]=min(dp[i+1][nde],dp[i][j]); 38 } 39 dp[i+1][0]=min(dp[i+1][0],dp[i][0]+1); 40 int nde=trie[0][(int)talk[i+1]]; 41 if(nde) dp[i+1][nde]=min(dp[i+1][nde],dp[i][0]); 42 } 43 printf("%d",dp[m][0]); 44 return 0; 45 }