-
题目描述:
-
给定一个由小写字母组成的字符串,求它的所有连续子串中,出现过至少两次,且至少有一对出现的重复子串是不重合的连续子串个数。
如给定字符串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
#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\n", ans);
}
return 0;
}