使用KMP寻找最长的前缀的方法,比一般的暴力法有快了很多。
本题一般的暴力法需要的是O(m*n*n*n),其中m是有多少字符串,而n是字符串长度,而使用KMP就可以把时间效率提高到O(m*n*n),减少了一个n,提高了一个档次啦。
速度快很多。
准确来说应该是利用KMP寻找一个字符串A,在另一个字符串B任意位置出现的A的最长的前缀字符串。
理解好KMP的next table就好办了。每次查找到相等字符的时候,保存好最长的前缀。
注意本题的条件:选取最前的字典顺序输出。老害我错的条件。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const int MAX_N = 61;
const int MAX_M = 11;
const int ALP_LEN = 4;
const int SIGNIFICANT = 3;
char dict[MAX_M][MAX_N];
int kmpTable[MAX_N];
int n;
inline int max(int a, int b) { return a > b ? a : b; }
inline int min(int a, int b) { return a < b ? a : b; }
int longestPrefix(char *chs, int len)
{
memset(kmpTable, 0, sizeof(int)*len);
for (int i = 1, j = 0; i < len; i++)
{
if (chs[i] == chs[j]) kmpTable[i] = ++j;
else if (j > 0)
{
j = kmpTable[j-1];
i--;
}
}
int L = MAX_N - 1;
for (int i = 1; i < n; i++)
{
int tmpAns = 0;
for (int k = 0, j = 0; k < L && j < len; k++)
{
if (chs[j] == dict[i][k])
{
++j;
tmpAns = max(tmpAns, j);
}
else if (j > 0)
{
j = kmpTable[j-1];
k--;
}
}
len = min(len, tmpAns);//optimize
}
return len;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
getchar(); // get rid of '\n'
for (int i = 0; i < n; i++)
{
gets(dict[i]);
}
int len = MAX_N - 1;
int maxLen = 0;
int pos = 0;
char res[MAX_N] = {'\0'};
char subStr[MAX_N] = {'\0'};
for (int i = 0; i < len; i++)//search every start position
{
int tmpLen = longestPrefix(dict[0]+i, len-i);
if (tmpLen >= maxLen)
{
if (tmpLen > maxLen)
{
maxLen = tmpLen;
pos = i;
}
else//irritating alphabetic order
{
strncpy(res, dict[0]+pos, maxLen);
strncpy(subStr, dict[0]+i, tmpLen);
if (strcmp(res, subStr))
{
maxLen = tmpLen;
pos = i;
}
}
}
}
if (maxLen < SIGNIFICANT)
{
puts("no significant commonalities");
}
else
{
strncpy(res, dict[0]+pos, maxLen);
printf("%s\n", res);
}
}
return 0;
}