[題解] P3082 [USACO13MAR]Necklace G
阿新 • • 發佈:2021-08-12
[題解] [USACO13MAR]Necklace G
前置知識
- AC 自動機。
如果你不會,可以看看 這篇文章(我不會告訴你我是來推銷部落格的。
請確保您已經學會 AC 自動機。
解題報告
對於題中的兩個字串,顯然奶牛的名字是模式串,我們先把它插入到 AC 自動機中。
沒有什麼正確的貪心做法,因此考慮 DP。
設題目中項鍊的字串為 \(S\),奶牛的名字為 \(S'\), \(f[i,j]\) 表示已經考慮了 \(S\) 的前 \(i\) 個字元,在 AC 自動機上對應的第 \(j\) 個結點的最大保留長度。
-
設 \(nxt\) 為上一個結點 \(j\) 的 \(i\) 指標,如果指向的不是 \(S'\)
-
同樣地,如果當前位置不是 \(S'\) 的結束位置,那麼 \(f[i,j]=max(f[i,j],f[i-1][j])\)。
最後答案取個 max,用總長度減去就好了。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; template <typename T> inline T read(){ T x=0;char ch=getchar();bool fl=false; while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();} while(isdigit(ch)){ x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); } return fl?-x:x; } #include <queue> const int maxn = 1000 + 10; int f[maxn*10][maxn]; namespace AC{ int t[maxn][26],fail[maxn],end[maxn],cnt=0; void insert(char *s){ int u=0; for(int i=1;s[i];i++){ if(!t[u][s[i]-'a'])t[u][s[i]-'a']=++cnt; u=t[u][s[i]-'a']; } end[u]=1; } queue<int> q; void build(){ for(int i=0;i<26;i++)if(t[0][i])q.push(t[0][i]); while(q.size()){ int u=q.front();q.pop(); for(int i=0;i<26;i++){ if(t[u][i])fail[t[u][i]]=t[fail[u]][i],q.push(t[u][i]); else t[u][i]=t[fail[u]][i]; } } } } using namespace AC; char s[maxn],T[maxn*10]; int main(){ cin>>T+1>>s+1; insert(s);build(); int len=strlen(T+1); for(int i=1;i<=len;i++){ for(int j=0;j<=cnt;j++){ if(!end[t[j][T[i]-'a']]) f[i][t[j][T[i]-'a']]=max(f[i][t[j][T[i]-'a']],f[i-1][j]+1); if(!end[j]) f[i][j]=max(f[i][j],f[i-1][j]); } } int ans=0; for(int i=0;i<=cnt;i++)ans=max(ans,f[len][i]); printf("%d\n",len-ans); return 0; }
小結
個人認為 AC 自動機只是簡化了 DP 的過程,並且便於理解。
本質上和其它題解的線性 DP 還是一樣的。