题目链接
对于这个题目,学习字符串hash技术。
首先一般用131进制数打hash表。我们可以再O(n)打出hash表,还可以O(logn)查询最长公共先/后缀。
对于每一个区间[l,r]的子串,其hash值是
f[r] - f[l-1] * 131^r - l + 1
说起来抽象,但是实际上比较简单,直接上代码吧
下面是ac代码:
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <cstring>
#include <queue>
#include <cmath>
#include <cstdio>
#define ll long long
using namespace std;
typedef unsigned long long ull;
ull a[1024][1024], b[1024][1024];
ull p[1024];
char su[1024][1024];
int dp[1024][1024];
void init(int n)
{
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
dp[i][j] = 1;
}
void _hash(int n)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
a[i][j] = a[i][j-1] * 131 + (su[i][j] - 'a' + 1);//打hash表
}
}
for (int i = 1; i <= n; i++)
{
for (int j = n; j >= 1; j--)
{
b[j][i] = b[j+1][i] * 131 + (su[j][i] - 'a' + 1);//打hash表
}
}
}
void initp()
{
p[0] = 1;
for (int i = 1; i <= 1020; i++)
p[i] = p[i-1] * 131;
}
int fi(int i, int j, int k)
{
int l = 1, r = k;
int ans = 0;
//二分寻找最长公共前缀
while(l<=r)
{
int mid = (l + r) >> 1;
ull ac = a[i][j+mid] - a[i][j-1] * p[mid+1];
ull bc = b[i-mid][j] - b[i+1][j] * p[mid+1];
if (ac == bc)
{
l = mid + 1;
ans = mid;
}
else r = mid - 1;
}
return ans + 1;
}
int main()
{
int n;
initp();
int mmax = 1;
while(cin >> n,n)
{
init(n);
mmax = 1;
for (int i = 1; i <= n; i++)
{
scanf("%s", su[i] + 1);
}
_hash(n);
for (int i = 2; i <= n; i++)
{
for (int j = n-1; j >= 1; j--)
{
int k = dp[i-1][j+1];
ull ac = a[i][j+k] - a[i][j-1] * p[k+1];
ull bc = b[i-k][j] - b[i+1][j] * p[k+1];//查询子串hash值
if (ac == bc)//如果hash值一样,两者相同。
dp[i][j] = k + 1;
else
{
int m = fi(i,j,k);//如果不同,二分找最长公共前缀。
// cout << i << " " << j << ":" << k << " " << m <<endl;
dp[i][j] = m;
}
mmax = max(mmax, dp[i][j]);
}
}
/*
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
cout <<dp[i][j] << " ";
cout << endl;
}
*/
printf("%d\n", mmax);
}
return 0;
}