- 题目描述:
-
给定一个由小写字母组成的字符串,求它的所有连续子串中,出现过至少两次,且至少有一对出现的重复子串是不重合的连续子串个数。
如给定字符串aaaa,aa和a,符合条件,aaa不符合条件(出现重合),故答案为2。
- 输入:
-
输入包含多组测试用例,每组测试用例包含一个字符串,由小写字母组成,其长度不大于1000。
- 输出:
-
对于每组测试数据,输出一个整数,代表符合条件的子串个数。
- 样例输入:
-
aaaa aaa
- 样例输出:
-
2 1
刚开始的时候,想到遍历每个字符子串,比如从位置1开始,依次遍历长度从1到n的子串,需要N^2。然后从位置2开始,依次遍历长度从1到n的子串,需要N^2。如果依次遍历完,总共O(N^3).字符串最长为1000,明显超过10^7数量级。就没有向下思考。
但是,仔细观察题目要求,会发现字符串是由小写字母组成的。那么如果长度很长,那么必然会有大量的重复字符串。而且,题目要求我们只需要找出满足条件的字符子串种类型,所以,如果我们采用枚举遍历,将曾经遍历过的字符子串标记出来,那么以后碰见这样的字符子串,就不需要重复遍历了。这样,必然可以讲数量级直接降到很低。
然后我们观察,如果i开始,长度为l的字符子串没有匹配,那么i开始,长度为l+1的,直接break,跳到i+1。这样又可以剪枝。
思路:穷举+剪枝,(1).枚举起点i和长度L,mark[i][L]表示这个字符串是否处理过,处理过不再处理,(2).若i位置上长L的串不可以,则更长的串也不可以,直接break L到下一个i
AC代码:#include <stdio.h> #include <string.h> const int MAX = 1002; int main() { bool mark[MAX][MAX]; char str[MAX]; while(scanf("%s", str) != EOF) { int len = strlen(str); for(int i = 0; i <= len ; i++) { for(int j = 0; j <= len; j++) mark[i][j] = false; } int ans = 0; for(i = 0; i < len; i++) { //如果i开始,j的长度的子串超过了字符串长度,那么,应该直接跳过。 for(int j = 1; j < len && i + j <= len; j++) { int hasMatch = 0; //如果从i开始,长度为j的字符串子串检查过,那么直接开始j+1的子串检查 if(mark[i][j] == true) continue; //对从i开始,长度为j的字符串子串进行遍历。 for(int k = i + j; k <= len - j; k ++) { //对已经标记过得子串(k,j),说明以前已经被匹配过,就不能再次进行匹配了 if(mark[k][j] == true) continue; if(strncmp(str + i, str + k, j) == 0) { mark[k][j] = true; hasMatch = 1; } } //遍历一遍之后进行统计 mark[i][j] = true; ans += hasMatch; if(hasMatch == 0) break; } } printf("%d ", ans); } return 0; }