解题:USACO07FEB The Cow Lexicon

题面

第一次做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 }
View Code

 

posted @ 2018-10-08 21:42  Speranza_Leaf  阅读(150)  评论(0)    收藏  举报