HDU 4534

AC自动机+状态DP。

虽然很明显的AC自动机+状态DP题,但要分析问题上还是欠缺一点了。一直在犹豫枚举每一个字符选或不选的状态会不会超时,以为会达到状态有2^n,但其实根本没有。因为有很多状态是可以重复的,这是由于每一位字符选或不选,都只能转移动自动机的固定位置,所以,状态是有限的。状态压缩是为了确定必须出现的串是否全部出现。于是可以设状态:

dp[i][j][k]为当前处理到第i个字符,处于自动机的j状态,k为必须出现的字符串是否出现的情况。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int root=0;
const int inf=1<<30;
int trie[1650][26];
int fail[1650];
int tag[1650],tot;
int que[1650],head,tail;
int kill[1650],must[1650],score[1650];
int need;
struct Status{
	int act,sc;
	void init(){
		act=inf,sc=-inf;
	}
}dp[2][1650][260];
char str[110];

void init(int cur){
	for(int i=0;i<=tot;i++){
		for(int k=0;k<(1<<(need+1));k++)
		dp[cur][i][k].init();
	}
}

void AddTrie(char *s,int sc){
	int len=strlen(s);
	int i=0,p=root;
	while(len--){
		if(trie[p][s[i]-'a']==-1){
			trie[p][s[i]-'a']=++tot;
			kill[tot]=must[tot]=0;
			memset(trie[tot],-1,sizeof(trie[tot]));
		}
		p=trie[p][s[i]-'a'];
		i++;
	}
	if(sc==999){must[p]|=(1<<need);}
	else if(sc==-999) kill[p]=1;
	else {
		score[p]+=sc;
	}
}

void build_ac(){
    head=tail=0;
    que[tail++]=root;
    while(head!=tail){
        int tmp=que[head++];
        int p=-1;
        for(int i=0;i<26;i++){
            if(trie[tmp][i]!=-1){
                if(tmp==root) fail[trie[tmp][i]]=root;
                else{
                    p=fail[tmp];
                    while(p!=-1){
                        if(trie[p][i]!=-1){
                            fail[trie[tmp][i]]=trie[p][i];
                            break;
                        }
                        p=fail[p];
                    }
                    if(p==-1) fail[trie[tmp][i]]=root;
                }
                if(kill[fail[trie[tmp][i]]]) kill[trie[tmp][i]]=1;
                if(must[fail[trie[tmp][i]]]) must[trie[tmp][i]]|=must[fail[trie[tmp][i]]];
                if(score[fail[trie[tmp][i]]]) score[trie[tmp][i]]+=score[fail[trie[tmp][i]]];
                que[tail++]=trie[tmp][i];
            }
            else{
                if(tmp==root) trie[tmp][i]=root;
                else{
                    p=fail[tmp];
                    while(p!=-1){
                        if(trie[p][i]!=-1){
                            trie[tmp][i]=trie[p][i];
                            break;
                        }
                        p=fail[p];
                    }
                    if(p==-1) trie[tmp][i]=root;
                }
            }
        }
    }
}

void Cal(){
	int cur=0,next=1;
	init(cur); init(next);
	int len=strlen(str);
	dp[cur][0][0].act=0; dp[cur][0][0].sc=0;
	for(int i=0;i<len;i++){
//		cout<<i<<endl;
		for(int j=0;j<=tot;j++){
			for(int k=0;k<(1<<(need+1));k++){
				if(dp[cur][j][k].act>=inf) continue;
				int pn=trie[j][str[i]-'a'];
				if(kill[pn]){
					if(dp[cur][j][k].act+1<dp[next][j][k].act){
						dp[next][j][k].act=dp[cur][j][k].act+1;
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
					else if(dp[cur][j][k].act+1==dp[next][j][k].act&&dp[cur][j][k].sc>dp[next][j][k].sc){
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
				}
				else{
					int tmpk=k|must[pn];
					if(dp[cur][j][k].act<dp[next][pn][tmpk].act){
						dp[next][pn][tmpk].act=dp[cur][j][k].act;
						dp[next][pn][tmpk].sc=dp[cur][j][k].sc+score[pn];
					}
					else if(dp[cur][j][k].act==dp[next][pn][tmpk].act&&dp[cur][j][k].sc+score[pn]>dp[next][pn][tmpk].sc){
						dp[next][pn][tmpk].sc=dp[cur][j][k].sc+score[pn];
					}
					if(dp[cur][j][k].act+1<dp[next][j][k].act	){
						dp[next][j][k].act=dp[cur][j][k].act+1;
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
					else if(dp[cur][j][k].act+1==dp[next][j][k].act&&dp[cur][j][k].sc>dp[next][j][k].sc){
						dp[next][j][k].sc=dp[cur][j][k].sc;
					}
				}
			}
		}
		
		init(cur);
		cur^=1;
		next^=1;
	}
	int minact=inf,maxsc=-inf;
	for(int i=0;i<=tot;i++){
			if(dp[cur][i][(1<<(need+1))-1].act<minact){
				minact=dp[cur][i][(1<<(need+1))-1].act;
				maxsc=dp[cur][i][(1<<(need+1))-1].sc;
			}
			else if(dp[cur][i][(1<<(need+1))-1].act==minact&&maxsc<dp[cur][i][(1<<(need+1))-1].sc)
			maxsc=dp[cur][i][(1<<(need+1))-1].sc;
	}
	if(minact>=inf)
	puts("Banned");
	else cout<<minact<<" "<<maxsc<<endl;
}


int main(){
	int T,icase=0;
	char s[20];int sc;
	scanf("%d",&T);
	while(T--){
		int n;
		tot=0,need=-1;
		memset(trie[tot],-1,sizeof(trie[tot]));
		memset(score,0,sizeof(score));
		memset(kill,0,sizeof(kill));
		memset(must,0,sizeof(must));
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%s%d",s,&sc);
			if(sc==999) need++;
			AddTrie(s,sc);
		}
//		cout<<"YES"<<endl;
		build_ac();
		scanf("%s",str);
		printf("Case %d: ",++icase);
		Cal();
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/jie-dcai/p/4419725.html