poj 1226
题目大意:给出一组字符串,要你求出这些字符串的最长公共子串的长度,公共子串可以正序或逆序匹配
解题思路:kmp枚举,枚举第一个字符串的每个子串的正序和逆序,与其余的字符串匹配看是否是其子串,求出最大的子串长度。
这题也可以用后缀树求,留待后面研究。。。。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 101;
char str[maxn][maxn];
int next[maxn], rnext[maxn], t, n;
void get_next(int start, int end, bool flag);
bool kmp(int index, int start, int end, bool flag);
int main()
{
scanf("%d", &t);
while(t-- != 0)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%s", str[i]);
int len = strlen(str[0]), ans = 0;
for(int i = 0; i < len; i++)
{
int l;
for(int j = i; j < len; j++)
{
l = j - i + 1;
get_next(i, j, true);
get_next(i, j, false);
int cnt = 1;
for(int k = 1; k < n; k++)
{
if(kmp(k, i, j, true) || kmp(k, i, j, false))
cnt++;
else
break;
}
if(cnt == n && ans < l)
ans = l;
}
}
printf("%d\n", ans);
}
return 0;
}
void get_next(int start, int end, bool flag)
{
int i, j;
if(flag)
{
next[start] = start - 1;
i = start; j = start - 1;
while(i <= end)
{
if(j == start - 1 || str[0][i] == str[0][j])
{
i++;
j++;
next[i] = j;
}
else
j = next[j];
}
}
else
{
rnext[end] = end + 1;
i = end; j = end + 1;
while(i >= start)
{
if(j == end + 1 || str[0][i] == str[0][j])
{
i--;
j--;
rnext[i] = j;
}
else
j = rnext[j];
}
}
}
bool kmp(int index, int start, int end, bool flag)
{
int i, j, k, len;
len = strlen(str[index]);
if(flag)
{
for(i = 0, j = start; i < len && j <= end;)
{
if(j == start - 1 || str[index][i] == str[0][j])
i++, j++;
else
j = next[j];
}
if(j > end)
return true;
}
else
{
for(i = 0, j = end; i < len && j >= start;)
{
if(j == end + 1 || str[index][i] == str[0][j])
i++, j--;
else
j = rnext[j];
}
if(j < start)
return true;
}
return false;
}
poj 1961
题目大意,给出一个字符串,若1~~i长的子字符串,是由几个相同的字符串挂接拼凑而成,从小到大输出i和重复字符串的个数
解题思路,kmp,利用kmp中next数组的特性,当i字符不匹配时,移到next[i]位置做比较,所以同一字符串里1~~next[i] - 1 和 i - (next[i] - 1) - 1 ~~~i字符串是匹配的,求出1~i是否由某个字符串连续拼接而成,只要知道i是否能整除next[i]~~~i的长度
如上图,第一副图,红色区域代表字符串是匹配的,那么next[i]~~~i为中间的白色加后面的红色区域,肯定不能被i整除
第二幅图是字符串第一个白色区域加绿色区域和 绿色区域加第二个白色区域的字符串匹配,根据next数组的特性1~~next[i] - 1 和 i - (next[i] - 1) - 1 ~~~i字符串是匹配的,那么第一块绿色区域的字符串和前面的白色匹配即1~~k 和 i - (next[i] - 1) - 1 ~ i - (next[i] - 1) - 1 + k -1 匹配。如此推导下去,每块区域都是相互匹配的即next[i]~i(最后的白色区域)能被i整除
另外一种情况时1~~k和k+1~~i匹配,显然能被i整除
所以判断的条件就是是否next[i]~i能被i整除
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000010;
char str[maxn];
int next[maxn], n;
void get_next();
int main()
{
int test = 1;
while(true)
{
scanf("%d", &n);
if(n == 0)
break;
printf("Test case #%d\n", test);
scanf("%s", str);
get_next();
for(int i = 1; i < n; i++)
{
int k, len;
len = i + 1;
if(len % (len - next[len]) == 0)
{
if( (k = len / (len - next[len])) > 1)
printf("%d %d\n", i + 1, k);
}
}
printf("\n");
test++;
}
return 0;
}
void get_next()
{
int i = 0, j = -1;
next[0] = -1;
while(i < n)
{
if(j == -1 || str[i] == str[j])
{
i++; j++;
next[i] = j;
}
else
j = next[j];
}
}