首页 > 技术文章 > [JSOI2009]密码

NP2Z 2021-06-09 09:48 原文

AC自动机 + DP套路 + 暴搜

首先看到题目之后,发现给出的字符串当中有前缀后缀的相连关系,所以大概就可以认定,这个是一个关于AC自动机的题目。

根据以往做AC自动机的经验来看, 非简单多字符串匹配的问题一般来说都需要进行fail树上DP ,再看看数据范围,惊奇地发现 \(1\leq L \leq 25\),并且 \(1\leq n \leq10\),一眼状态压缩,怎么压? 压的是这 \(n\) 个串的取舍情况,细致来说,就是在 \(n\) 位二进制下,若第 \(i\) 个字符串要取得,那么就将二进制的第 \(i\) 位设置成 \(1\),其实也就是 (1<<(ith-1))

其实会一点状态压缩知识的同学都可以直接跳过上面一段,因为 \(AC\) 自动机 \(fail\) 树上的 \(DP\)一般来说都很套路,所以我们还是考虑 \(f_{i,j,st}\)经过了自动机上的 \(i\) 个点(就是已经得到的串的长度), \(j\) 为现在所在的节点编号, \(st\) 就是现在的取舍状态,所以得到方程:

\[f_{i+1,j,st|state_i} = \sum_{i,j} f_{i,j,st} \]

方程一出,那么也就比较好解了。

但是,我们还落下一个很重要的一问 ,就是在 \(ans \leq 42\) 的时候输出方案,这该咋搞呢???

考虑 \(\huge{\text{暴搜}}\)

就相当于把这到题目当成两个问题来做就好了, \(ans \leq 42\)很小,所以直接暴搜枚举,再按照字典序排列输出就ok了。

code

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define debug cout<<"debug"<<endl
#define int long long
namespace xin_io
{
	#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
	#define scanf eat1 = scanf 
	#define freopen eat2 = freopen
	char buf[1<<20],*p1 = buf,*p2 = buf;FILE *eat2;
	inline void openfile() {freopen("t.txt","r",stdin);} inline void outfile(){freopen("o.txt","w",stdout);}
	inline int get()
	{
		int s = 0,f = 1;register char ch = gc();
		while(!isdigit(ch))
		{if(ch == '-') f = -1;ch = gc();}
		while(isdigit(ch))
		{s = s * 10 + ch - '0';ch = gc();}return s * f;
	}
}
using namespace xin_io; int eat1;
static const int maxn = 102;
namespace xin
{
	char s[11][11];
	int l,n,state[maxn],ans = 0;
	class xin_ac
	{
		public:
			class xin_trie{public:int son[26],fail;}t[maxn];
			int tot;
			xin_ac():tot(0){}
			inline void insert(char *s,int ith)
			{	
				int len = strlen(s);
				register int u = 0,bian;
				for(register int i=0;s[i];++i)
				{
					bian = s[i] - 'a';
					if(!t[u].son[bian]) t[u].son[bian] = ++tot;
					u = t[u].son[bian];
//					if(i == len) state[u] |= (1 << (ith - 1));
				}
				state[u] |= (1 << (ith - 1));
			}
			
  	 		void getFail() 
			{
   			    queue < int > q;
   			    for (register int c = 0; c < 26; ++c) 
				{
   			        int v = t[0].son[c];
   			        if (v) q.push(v), t[v].fail = 0;
   	   		 	}
   	   			while (!q.empty()) 
				{
   	   				register int u = q.front(); q.pop();
   	   		     	for (register int c = 0;c<26;++c) 
					{
   	   		     	    int v = t[u].son[c];
   	   		     	    if (!v) {t[u].son[c] = t[t[u].fail].son[c]; continue ;}
   	       		 	    q.push(v);
   	       		 	    int j = t[u].fail;
   	        		    while (j and !t[j].son[c]) j = t[j].fail;
   	        	   		t[v].fail = t[j].son[c], state[v] |= state[t[v].fail];
   	        	 	}
	       	 }
	    }

	}ac;
	int f[30][1028][maxn],m;
	int temp[20], vis[20], lans[20][20], tot = 0;
    string str[50], tmp;
    void dfs(int a) 
    {
        if (a == m + 1) 
        {
        	tmp.clear();
            for(register int i = 1; i <= m; ++i) 
            {
            	int st = 0, num = strlen(s[temp[i]]);
            	if (i != 1) st = lans[temp[i - 1]][temp[i]];
            	for(register int j = st; j < num; ++j) 
            		tmp += s[temp[i]][j];
			}
			if(tmp.size() == n) str[++tot] = tmp;
            return ; 
        }
        for(register int i = 1; i <= m; ++i) 
            if (!vis[i]) 
            {
            	temp[a] = i; vis[i] = true; 
            	dfs(a + 1);
            	vis[i] = false, temp[a] = 0;
            }
    }
	inline short main()
	{
	#ifndef ONLINE_JUDGE
		openfile();
	#endif
		scanf("%lld%lld",&l,&n);
		for(register int i=1;i<=n;++i)
		{
			scanf("%s",s[i]);
			ac.insert(s[i],i);
		}
		ac.getFail();
//		for(register int i=0;i<=ac.tot;++i)
//			cout<<ac.t[i].fail<<' '<<state[i]<<endl;
		f[0][0][0] = 1;
		for(register int i=0;i<=l;++i)
			for(register int st=0;st<(1<<n);++st)
				for(register int u=0;u<=ac.tot;++u)
					if(f[i][st][u])
						for(register int j=0;j<26;++j)
							f[i+1][st | state[ac.t[u].son[j]]][ac.t[u].son[j]] += f[i][st][u];
		for(register int u=0;u<=ac.tot;++u)  ans += f[l][(1 << n)-1][u];
		cout<<ans<<endl;
		m = n; n = l;
		if(ans <= 42)
		{
    		for(register int x = 1; x <= m; ++x) 
    		{
    			for(register int y = 1; y <= m; ++y) 
    			if (x != y) 
    			{
    				register int lx = strlen(s[x]), ly = strlen(s[y]);
    				for(register int len = min(lx, ly); len >= 0; --len) 
    				{
    					bool ok = 0;
    					for(register int i=0;i<len; ++i) 
    					{
    						int j = lx - len + i;
    						if (s[x][j] != s[y][i]) {ok = true; break;} 
						}
						if(!ok) 
						{
							lans[x][y] = len;
							break;
						}
					}
				}
			}
        	dfs(1);
        	sort(str+1,str+1+ans);
        	for(register int i = 1;i<=ans;++i) cout << str[i] << endl;
        }
		return 0;
	}
}
signed main() {return xin::main();}

推荐阅读