废话:
这道题很是花了我一番功夫。首先,我不会kmp算法,还专门学了一下这个算法。其次,即使会用kmp,但是如果暴力枚举的话,还是毫无疑问会爆掉。因此在dfs的基础上加上两次剪枝解决了这道题。
题意:
我没有读题,只是队友给我解释了题意,然后我根据题意写的题。
大概意思是给n个字符串,从上到下依次标记为1——n,寻找一个标记最大的串,要求这个串满足:标记比它小的串中至少有一个不是它的子串。
输入:
第一行输入一个整型t,表示共有t组数据。
每组数据首行一个整型n,表示有n个串。
接下来n行,每行一个字符串。
输出:
输出格式为”Case #x: y”,其中x为组数,y表示串的标记。
如果存在满足条件的串,则输出这个串的标记,否则输出-1。
题解:
- 使用dfs,源串从标记最大的串(串n-1)开始,匹配串从比源串小的串开始,从大到小依次匹配。
- 如果两个串匹配,则继续匹配更小的匹配串。如果两个串不匹配,则记录不匹配的匹配串,同时比较源串和答案的大小,答案取较大值,同时执行3。如果匹配串直到匹配到标记最小的串(串0),所有串都可以和源串匹配,则执行4。
- 源串回溯,然后用回溯的源串和记录的匹配串进行比较,即,进行2。
- 当前源串不满足条件,回溯到dfs结束。
- 输出答案。
需要注意的有两点——
- 如果当前源串Aj与当前匹配串Ai不匹配,那么可知当前源串的父串Ak,即当前源串的源串,也可以直接与Ai比较,而不需要与Aj和Ai之间的串匹配,因为Al(i < l < j) 一定是Ak的子串。这点很容易证明。
- 如果源串Ak一直递归到最小的串A0都匹配,那么任意串Ai(0 < i <= k) 都和A0匹配,即A0是任意串Ai的子串。那么任意Ai都不可能是答案。
代码如下——
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 7 const int N = 2010; 8 9 int Next[N]; 10 char s[510][N]; 11 int t, n; 12 int lenS, lenT; 13 int ans; 14 int mid; //剪枝,放置某个串被多次访问 15 16 void init() 17 { 18 scanf("%d", &n); 19 for(int i = 0; i < n; i++) scanf("%s", s[i]); 20 ans = -2; 21 mid = -1; 22 } 23 24 void kmpNext(char* T) //计算Next数组 25 { 26 int i = 1; 27 Next[0] = -1; 28 while(i < lenT) 29 { 30 int j = 0; 31 while(T[j] == T[i]) 32 { 33 Next[i] = j; 34 i++; 35 j++; 36 } 37 Next[i] = j; 38 i++; 39 } 40 } 41 42 bool kmp(char* S, char* T) //kmp算法 43 { 44 lenS = strlen(S); 45 lenT = strlen(T); 46 kmpNext(T); 47 int i = 0, j = 0; 48 while(i < lenS && j < lenT) 49 { 50 if(j == -1) 51 { 52 i++; 53 j = 0; 54 } 55 else if(S[i] == T[j]) 56 { 57 i++; 58 j++; 59 } 60 else j = Next[j]; 61 } 62 if(j == lenT) return 1; 63 return 0; 64 } 65 66 bool dfs(int x) //递归寻找子串 67 { 68 for(int i = x-1; i >= 0; i--) 69 { 70 if(mid != -1 && mid != i) continue; //剪枝,如果串Ai从未查询过,或者串Ax的子串含不串Ai,那么查询Ax是否含Ai 71 if(kmp(s[x], s[i])) 72 { 73 if(i == 0) return 1; 74 mid = -1; //如果串Ax含串Ai,那么mid归为-1 75 bool flag = dfs(i); 76 if(flag) return 1; //剪枝,如果串A0是串Ax的子串,那么Ax不符合条件 77 } 78 else 79 { 80 ans = ans > x ? ans : x; //永远取最大值 81 mid = i; 82 break; 83 } 84 85 86 } 87 return 0; 88 } 89 90 void work() 91 { 92 dfs(n-1); 93 } 94 95 void outit(int tm) 96 { 97 printf("Case #%d: %d\n", tm, ans+1); 98 } 99 100 int main() 101 { 102 scanf("%d", &t); 103 for(int tm = 1; tm <= t; tm++) 104 { 105 init(); 106 work(); 107 outit(tm); 108 } 109 }
Kmp算法——
http://www.cnblogs.com/mypride/p/4950245.html
ps. 看了看别人的代码,有些直接用了迭代法,复杂度为n^2/2,不过加上了一个感觉不太靠谱的剪枝。我算了算,最坏情况下时间好像还有可能爆掉。但是运行结果竟然不比我的慢多少,醉了。可能我算时间的方法有问题?或者数据特殊?
唉……革命尚未成功,同志仍需努力啊!
时间: 2024-10-25 02:13:41