[题解] P3082 [USACO13MAR]Necklace G

· · 题解

[题解] [USACO13MAR]Necklace G

传送门

前置知识

如果你不会,可以看看 这篇文章(我不会告诉你我是来推销博客的。

请确保您已经学会 AC 自动机。

解题报告

对于题中的两个字符串,显然奶牛的名字是模式串,我们先把它插入到 AC 自动机中。

没有什么正确的贪心做法,因此考虑 DP。

设题目中项链的字符串为 S,奶牛的名字为 S'f[i,j] 表示已经考虑了 S 的前 i 个字符,在 AC 自动机上对应的第 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 还是一样的。