zoukankan      html  css  js  c++  java
  • 【YBTOJ】【AC自动机】组合攻击

    组合攻击

    小明在玩一款游戏,该游戏只有三个技能键 ( exttt{A})( exttt{B})( exttt{C}) 可用,但这些键可用形成 (n) 种特定的组合技。第 (i) 个组合技用一个字符串 (s_i) 表示。

    小明会输入一个长度为 (k) 的字符串 ,而一个组合技每在 (t) 中出现一次,小明就会获得一分。 (s_i)(t) 中出现一次指的是 (s_i)(t_i) 从某个位置起的连续子串。如果 (s_i)(t) 的多个位置起都是连续子串,那么算作 (s_i) 出现了多次。

    若小明输入了恰好 (k) 个字符,则他最多能获得多少分。

    (n leq 20)(kleq10^3)(1leq|s_i|leq15)

    题解

    AC自动机 + DP 模板。

    先根据每个组合技建立出 AC自动机的trie图 ,然后根据此自动机进行 dp 。

    设: (dp(i,j)) 表示,当前填入第 (i) 位,填入的字符在 trie树 中的点编号是 (j) ,能得到的最大答案。

    那么有 dp 式:

    [dp(i,c_{j,k}) = max{dp(i-1,j)+val(c_{j,k})} ]

    其中 (val(x)) 表示 在 trie树 上匹配到第 (x) 号节点,有多少个字符串结束(对答案的贡献)。

    这里注意:在 BFS 的过程中,要 $val(p) leftarrow val(p) +val(fail_p) $ , 因为匹配到 (p) 点的同时必定匹配到 (fail_p)

    代码

    #include <bits/stdc++.h>
    #define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    const int INF = 0x3f3f3f3f,N = 22,M = 17,L = 1e3+5;
    typedef long long ll;
    typedef unsigned long long ull;
    inline ll read(){
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9'))ch=c,c=getchar();
    	while(c>='0'&&c<='9')ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n,m;
    template <typename T> struct que{
    	T a[N*M]; int st=1,ed=0;
    	que(){st=1,ed=0;}
    	inline void clear(){st=1,ed=0;}
    	inline int size(){return ed-st+1;}
    	inline bool empty(){return !(ed-st+1);}
    	inline T front(){return a[st];}
    	inline T back(){return a[ed];}
    	inline void pop_front(){st++;}
    	inline void pop_back(){ed--;}
    	inline void push(T x){a[++ed] = x;}
    	inline T operator [] (int x){return a[st+x-1];}
    };
    int dp[L][N*M];
    struct ACauto{
    	int c[N*M][5],fail[N*M],tot = 1;
    	int w[N*M];
    	que <int> q;
    	inline void insert(char ch[]){
    		int p = 1 , len = strlen(ch+1);
    		for(int i = 1 ; i <= len ; i ++){
    			int v = ch[i]-'A'+1;
    			if(!c[p][v]) c[p][v] = ++tot;
    			p = c[p][v];
    		} 
    		w[p] ++;
    	}
    	void build(){
    		for(int i = 1 ; i <= 3 ; i ++) c[0][i] = 1;
    		q.push(1);
    		while(!q.empty()){
    			int p = q.front(); q.pop_front();
    			w[p] += w[fail[p]];
    			for(int i = 1 ; i <= 3 ; i ++)
    				if(c[p][i]) fail[c[p][i]] = c[fail[p]][i], q.push(c[p][i]);
    				else c[p][i] = c[fail[p]][i];
    		}
    	}
    	int Dp(){
    		for(int i = 1 ; i <= m ; i ++)
    			for(int j = 1 ; j <= tot ; j ++)
    				for(int k = 1 ; k <= 3 ; k ++)
    					dp[i][c[j][k]] = max(dp[i][c[j][k]],dp[i-1][j] + w[c[j][k]]);
    //					printf(" update [%d][%d]  from [%d,%d](%d) + [%d](%d) -> (%d)
    ",i,c[j][k],i-1,j,dp[i-1][j],c[j][k],w[c[j][k]],dp[i][c[j][k]]);
    		int ret = 0;
    		for(int i = 1 ; i <= tot ; i ++)
    			ret = max(ret,dp[m][i]);
    		return ret;
    	}
    }ac;
    char ch[M];
    signed main(){
    	n = read(), m = read();
    	for(int i = 1 ; i <= n ; i ++)
    		scanf("%s",ch+1),
    		ac.insert(ch);
    	ac.build();
    	memset(dp,0xc0,sizeof(dp));
    	for(int i = 0 ; i <= m ; i ++) dp[i][1] = 0;
    	printf("%d",ac.Dp());
    	return 0;
    }
    
  • 相关阅读:
    自己动手丰衣足食之轮播图一动态修改marginTop属性实现轮播图
    自己动手丰衣足食之 jQuery 数量加减插件
    Asp.net 中,在服务端向客户端写脚本的常用方法
    Asp.Net使用加密cookie代替session验证用户登录状态 源码分享
    Javascript技术之详尽解析event对象
    Javascript的V8引擎研究
    11个实用的CSS学习工具[转载收藏]
    为什么JavaScript声明变量的时候鼓励加var关键字
    asp.net菜鸟到中级程序员的飞跃 --30本好书点评
    创建安全的ashx文件,ashx编译
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/15330861.html
Copyright © 2011-2022 走看看