codeforces 569 E. New Language (2-sat)

  1/*************************************************************************
  2  > File Name: code/cf/#315/E.cpp
  3  > Author: 111qqz
  4  > Email: rkz2013@126.com 
  5  > Created Time: 2015年08月15日 星期六 13时48分36秒
  6 ************************************************************************/
  7#include<iostream>
  8#include<iomanip>
  9#include<cstdio>
 10#include<algorithm>
 11#include<cmath>
 12#include<cstring>
 13#include<string>
 14#include<map>
 15#include<set>
 16#include<queue>
 17#include<vector>
 18#include<stack>
 19#define y0 abc111qqz
 20#define y1 hust111qqz
 21#define yn hez111qqz
 22#define j1 cute111qqz
 23#define tm crazy111qqz
 24#define lr dying111qqz
 25using namespace std;
 26#define REP(i, n) for (int i=0;i<int(n);++i)  
 27typedef long long LL;
 28typedef unsigned long long ULL;
 29const int inf = 0x7fffffff;
 30const int N=5E2+7;
 31int flag[N],flag2[N];
 32int f[N][N];
 33int a[N];
 34char s[N];
 35int ans[N];
 36int len,n,m;
 37char s1[13],s2[13];
 38int p1,p2,q1,q2,dq1,dq2;
 39void add(int p,int q,int flag[])
 40{
 41    int dq = q * n + p;//找到元辅音状态为q,第p的点的下标
 42    for (int i=1;i<=2*n;++i)
 43    {
 44	if (f[dq][i]==0) continue;
 45	flag[i]=1;//找到所有由dp出发的边指向的点,表示选了dp点一定要选的点。
 46    }
 47} 
 48bool check(int flag[])
 49{
 50    for (int i=1;i<=n;++i) 
 51	if (flag[i]==1&&flag[i+n]==1) return false; //判断是否存在矛盾
 52    //(选了j点后,既要选择某点k的元音,也要选择某点k的辅音) 
 53    return true;
 54}
 55bool dfs(int pos,int x)
 56{
 57    if (pos>n) return true;//如果能形成一个长度为n的单词,说明这种语言有word
 58    bool g[2];
 59    g[0]=g[1]=false;
 60    for (int i=x;i<=len;++i)//从当前字母x往后枚举
 61    {
 62	for (int j=1;j<=2*n;++j) flag2[j]=flag[j];//为了不影响原始数组,复制一个布尔数组出来。
 63	add(pos,a[i],flag2);//找到所有选了pos点一定要选的点
 64	if (check(flag2)&&(!g[a[i]]))
 65	{
 66	    g[a[i]]=true;
 67	    for (int j=1;j<=2*n;++j) flag[j]=flag2[j];
 68	    ans[pos]=i;//将第pos位置的字母变成i
 69	    if (dfs(pos+1,1)) return true; 
 70	    else return false;//只要有一位找不到合适的字母形成单词,那么肯定就构不成单词。
 71	}
 72    }
 73    return false;
 74}
 75int main()
 76{
 77    scanf("%s",s);
 78    len=strlen(s);           //0表示辅音,1表示原因,下同。
 79    for (int i=1;i<=len;++i)//len 表示字母表中一共有的字母的个数
 80    {
 81	if (s[i-1]=='V')
 82	{
 83	    a[i] = 0;
 84	}
 85	else
 86	{
 87	    a[i]  = 1;
 88	}
 89    }
 90    memset(f,0,sizeof(f));
 91    scanf("%d%d",&n,&m);
 92    for (int i=1;i<=m;++i)//1..n表示元音的点,n+1..2*n 表示辅音的点
 93    {
 94	scanf("%d",&p1);
 95	scanf("%s",s1);
 96	if (s1[0]=='V') q1=0;else q1=1;
 97	scanf("%d",&p2);
 98	scanf("%s",s2);
 99	if (s2[0]=='V') q2=0;else q2=1;
100	dq1=q1*n+p1;dq2=q2*n+p2;//找到这组关系对应的点。
101	f[dq1][dq2]=1;//连一条由dq1指向dq2的边,表示如果选了dp1点,那么一定选dp2点   
102	dq1=(1-q2)*n+p2;dq2=(1-q1)*n+p1;//找到逆否命题对应的点 
103	// ("如果选1,一定选2"的逆否命题是,"如果不选2,一定不选1")
104	f[dq1][dq2]=1;      //在连一条边
105    }
106    for (int i=1;i<=2*n;++i) f[i][i]=1;
107    for (int k=1;k<=2*n;++k)//floyd ,把所有间接相连的边直接相连
108    {
109	for (int i=1;i<=2*n;++i)
110	    for (int j=1;j<=2*n;++j)
111		f[i][j]|=f[i][k]&f[k][j];
112    }
113    scanf("%s",s+1);
114    bool ok=false;
115    for (int i=n;i>=0;--i) //倒着扫,每次只改变最后一位的字母,字母从小往大枚举 //这样就可以保证字典序最小。
116    {
117	memset(flag,0,sizeof(flag));
118	for (int j=1;j<=i;++j) 
119	{
120	    add(j,a[s[j]-'a'+1],flag);	
121	    ans[j]=s[j]-'a'+1;
122	}
123	if (!check(flag)) continue;
124	if (dfs(i+1,s[i+1]-'a'+1+1))
125	{
126	    ok=true;
127	    break;
128	}		
129    }
130    if (!ok) printf("-1\n");
131    else
132    {
133	for (int i=1;i<=n;++i) printf("%c",ans[i]+'a'-1);
134    }
135    return 0;
136}