每一个字母可以变成两个字母,于是枚举变出的两个字母所继续形成的两个区间的分界点,然后就可以区间DP啦!
我的方程好像和大家的都不太一样?
记f[i][j]表示字符串区间[i,j]可能由什么字母变来。状压,f[i][j]的值用二进制存,从右往左依次表示W,I,N,G是否可取(如0010表示仅I可取)。
枚举分界点时,把两边字母合起来,对比一下有没有哪个字母可以变成这两个字母的。如果有就把这个字母转化成对应二进制之后,和f[i][j]或一下就行了。
#include<cstdio>
#include<iostream>
#include<cstring>
#define LEN 205
using namespace std;
char c[9]={0,'W','I',0,'N',0,0,0,'G'}, s[205], a, b;
int f[LEN][LEN], h[99999];
inline int find_hash(char a, char b)
{
int r=(a-45)*1000+(b-45);
return h[r];
}
inline void add_hash(char a, char b, int x)
{
int r=(a-45)*1000+(b-45);
h[r]|=x;
}
int main()
{
int cnt_w, cnt_i, cnt_n, cnt_g;
scanf("%d%d%d%d\n",&cnt_w, &cnt_i, &cnt_n, &cnt_g);
for(int i = 1; i <= cnt_w; i++){scanf("%c%c\n",&a,&b);add_hash(a,b,1);}
for(int i = 1; i <= cnt_i; i++){scanf("%c%c\n",&a,&b);add_hash(a,b,2);}
for(int i = 1; i <= cnt_n; i++){scanf("%c%c\n",&a,&b);add_hash(a,b,4);}
for(int i = 1; i <= cnt_g; i++){scanf("%c%c\n",&a,&b);add_hash(a,b,8);}
gets(s);
int len=strlen(s)-1;
for(int i = 0; i <= len; i++)
switch(s[i])
{
case 'W': f[i][i]=1;break;
case 'I': f[i][i]=2;break;
case 'N': f[i][i]=4;break;
case 'G': f[i][i]=8;break;
default : f[i][i]=0;break;
}
for(int d = 1; d <= len; d++)
for(int i = 0, j = i+d; j <= len; i++, j++)
for(int k = i; k < j; k++)
{
int l=f[i][k], r=f[k+1][j];
for(int li = 1; li <= 8; li<<=1)
{
if(!(li&l))continue;
for(int ri = 1; ri <= 8; ri<<=1)
{
if(!(ri&r))continue;
f[i][j]|=find_hash(c[li],c[ri]);
}
}
}
if(f[0][len]==0)printf("The name is wrong!\n");
else for(int i = 1; i <= 8; i<<=1)
{
if(f[0][len]&i)
cout<<c[i];
}
return 0;
}