题目大意:
有若干模式串,将某些模式串拼接起来(一个可以使用多次)形成一个长模式串,判断能否有两种或更多种不同的拼法拼成相同的模式串。
思路:
神奇的构图,暴力的求解。
可以发现,若有不同的拼法,则一个模式串的前缀要与一个模式串的后缀相同。
因此我们就将问题转化成:从两个模式串开始,不停的按照前后缀匹配,最后达到两个串同时在一个点结束。
那么,将每一个串的每一个字符都看作一个点,n2len2暴力枚举i串从z开始的后缀和j串(自己也可以,但不能让前缀是其本身)的前缀做匹配,看是否能将其中一个串匹配完。
当(i,z)和j匹配,若i先结束了,则将(i,z)这个点连边到j串没匹配的第一个点,表示下一次匹配应该是从j的那个位置开始当后缀再寻找其他前缀;如果j先结束,与之前一个类似;如果i和j同时结束,则连边到终点。
最后用dfs判断能否从某个模式串的第一个点开始走到终点。
PS:1、fromhttp://blog.csdn.net/houserabbit/article/details/38943645
2、数组的意义、范围要清楚,我花了一个多小时才发现数组开小了。
代码:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int M=109,N=30; 5 int n,e,t,i,j,k,z,cnt,v[M*M*N*N],id[M][N],nex[M*M*N*N],len[M],head[M*N]; 6 bool vis[M*N],flag; 7 char s[M][N]; 8 9 void add(int x,int y) { v[++cnt]=y,nex[cnt]=head[x],head[x]=cnt; } 10 11 void dfs(int x) 12 { 13 if (x==e) { flag=1; return; } 14 vis[x]=1; 15 for (int i=head[x];~i;i=nex[i]) 16 if (!vis[v[i]]) 17 { 18 dfs(v[i]); 19 if (flag) return; 20 } 21 } 22 23 int main() 24 { 25 while (~scanf("%d",&n)) 26 { 27 if (!n) break; 28 e=cnt=flag=0; 29 for (i=1;i<=n;++i) 30 { 31 scanf("%s%s",s[i],s[i]); 32 len[i]=strlen(s[i]); 33 for (j=0;j<len[i];++j) id[i][j]=++e; 34 } 35 for (++e,i=0;i<=e;++i) head[i]=-1; 36 for (i=1;i<=n;++i) 37 for (z=0;z<len[i];++z) 38 for (j=1;j<=n;++j) 39 { 40 if (i==j && !z) continue; 41 for (k=0;k<len[j] && z+k<len[i];++k) 42 if (s[i][z+k]!=s[j][k]) break; 43 if (z+k==len[i] && k==len[j]) add(id[i][z],e); 44 else if (k==len[j]) add(id[i][z],id[i][z+k]); 45 else if (z+k==len[i]) add(id[i][z],id[j][k]); 46 } 47 for (i=0;i<=e;++i) vis[i]=0; 48 for (i=1;i<=n;++i) 49 { 50 if (!vis[id[i][0]]) dfs(id[i][0]); 51 if (flag) break; 52 } 53 printf("Case #%d: ",++t); 54 if (flag) puts("Ambiguous."); else puts("Not ambiguous."); 55 } 56 return 0; 57 }