100道动态规划——30 UVAlive 3907 Puzzle AC自动机上的动态规划,记忆化搜索

        一开始确实没有想到怎么做。。

        定义状态dp[i]表示自动机上的节点i还能够附加多长的字符,因此每一个串的结尾字符在自动机上对应的dp为0,这就是初始条件

        状态转移方程就是dp[i]=max{dp[j]}+1,其中j表示i点可达的节点,最后的答案就是dp[root]

       输出No的情况有两个,一个是在记忆化搜索的过程中,走到了一个已经访问过的节点了,此时有环,长度为无穷。还有一个就是答案为空串的情况

       标记访问过了用一个vis数组就好,用一个flag标记记录是否有环


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int maxm=5E4+5;
int times,n,sz,root,s,ch[maxm][26],fail[maxm],dp[maxm],rec[maxm],newnode();
bool vis[maxm],val[maxm],flag;
char str[55];
void init();
void ins(int k);
void getfail();
void dfs(int node);

int main(){
    scanf("%d",×);
    while(times--){
        init();
        scanf("%d%d",&n,&s);
        for(int i=0;i<s;++i){
            scanf("%s",str);
            ins(i);
        }
        getfail();
        dfs(root);
        if(flag||!dp[ch[root][rec[root]]])
            printf("No\n");
        else{
            int u=root;
            while(dp[ch[u][rec[u]]]){
                printf("%c",rec[u]+'A');
                u=ch[u][rec[u]];
            }
            printf("\n");
        }
    }
    return 0;
}

void dfs(int node){
    if(vis[node]&&dp[node]==-1){
        flag=true;
    }
    else if(!vis[node]){
        vis[node]=true;
        int te=0;
        for(int i=0;i<n;++i){
            dfs(ch[node][i]);
            if(flag)
                return;
            if(te<=dp[ch[node][i]])
                te=dp[ch[node][i]],rec[node]=i;
        }
        dp[node]=te+1;
    }
}

void init(){
    sz=flag=0;
    root=newnode();
}

int newnode(){
    dp[sz]=-1;
    vis[sz]=rec[sz]=val[sz]=0;
    memset(ch[sz],0,sizeof ch[sz]);
    return sz++;
}

void ins(int k){
    int u=root;
    for(int i=0;str[i];++i){
        str[i]-='A';
        if(ch[u][str[i]]==root)
            ch[u][str[i]]=newnode();
        u=ch[u][str[i]];
    }
    val[u]=vis[u]=1;
    dp[u]=0;
}


void getfail(){
    queue<int> q;
    fail[root]=root;

    for(int i=0;i<n;++i)
    if(ch[root][i]!=root){
        fail[ch[root][i]]=root;
        q.push(ch[root][i]);
    }

    int u;
    while(!q.empty()){
        u=q.front();q.pop();
        for(int i=0;i<n;++i){
            if(ch[u][i]==root)
                ch[u][i]=ch[fail[u]][i];
            else{
                fail[ch[u][i]]=ch[fail[u]][i];
                q.push(ch[u][i]);
            }
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值