链接
题意
对每个case,给出N个单词(字符串),之后有Q组询问,对每组询问(i, j),给出第i个字符串和第j个字符串的最长公共前缀长度。
思路
这是一个典型的字符串哈希问题。字符串有很好的单调性,可以使其在匹配问题上结合二分提高效率。比如本问题,找两个字符串最长公共前缀,如果前缀j匹配,那么前缀i(i < j)一定是匹配的,利用二分能很快找到最长匹配的长度。
本题做法是对每个字符串求其前缀hash数组,对每个询问二分查找即可。
代码用了自然溢出哈希,但是没有被卡掉。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
using namespace std;
typedef unsigned long long ulint;
#define maxn (1000007)
const ulint seed = 30007uLL;
const ulint mod = 1e9 + 1017uLL;
ulint Hi[maxn], Hj[maxn];
string word[maxn];
vector<ulint> H[maxn];
int solve(int i, int j)
{
int ans = 0, l = 1, r = min(word[i].size(), word[j].size()), m;
vector<ulint> &hi = H[i];
vector<ulint> &hj = H[j];
while(l <= r)
{
m = (l + r) >> 1;
if(hi[m-1] == hj[m-1])
{
ans = m;
l = m + 1;
}
else r = m - 1;
}
return ans;
}
int main()
{
//freopen("12338.txt", "r", stdin);
cin.sync_with_stdio(false);
int T, kase = 0;
cin >> T;
while(T--)
{
printf("Case %d:\n", ++kase);
int N;
cin >> N;
for(int i = 1; i <= N; i++)
{
cin >> word[i];
string& s = word[i];
vector<ulint>& h = H[i];
h.clear();
h.push_back(s[0] - 'a' + 1);
for(int j = 1; j < s.size(); j++)
{
h.push_back(h[j-1] * seed + s[j] - 'a' + 1);
}
}
int Q, i, j;
cin >> Q;
while(Q--)
{
cin >> i >> j;
printf("%d\n", solve(i, j));
}
}
return 0;
}