题意:
有n个字符串,每个字符串长度不超过20,000。让你找出两个不同字符串中所隐藏的前缀字符串v,和后缀字符串w。
然后使得v连接|w|次等于w连接|v|次。|v|表示字符串v的长度。
输出|v|,|w|的结果。
如果有多种方案,让你输出两者和最大的。
思路:
hash+umap
看杰哥代码的。
将每个字符串都hash,并将它们的每个前缀字符串hash值都插入到umap中。
枚举每个字符串的后缀,检查以每个后缀作为基准的可行性,并将|v|,|w|最大化。
检查可行性:
找两个值来保存脑补前缀v和实际后缀w的hash值,一开始v和w是一样的。
然后通过查找umap中是否存在v的hash值来更新|v|的长度以及比较v和w来更新|w|的长度。
具体看代码。
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 205;
const int L = 2e4+5;
const int X = 233;
const int MOD = 1e9+7;
typedef long long LL;
int n;
char ch[N][L];
int hh[N][L];
int ax[L];
unordered_map <int, int> ump;
int get(int i, int l, int r) {
int len = r-l+1;
return (hh[i][l+len-1]-(LL)hh[i][l-1]*ax[len]%MOD+MOD)%MOD;
}
void solve() {
ump.clear();
for(int i = 0;i < n; i++) {
int len = strlen(ch[i]+1);
for(int j = 1;j <= len; j++) {
hh[i][j] = ((LL)hh[i][j-1]*X%MOD+ch[i][j])%MOD;
ump[hh[i][j]]++;
}
}
int r1 = 0, r2 = 0;
for(int i = 0;i < n; i++) {
int len = strlen(ch[i]+1);
for(int j = 1;j <= len; j++) ump[hh[i][j]]--;
for(int j = 1;j <= len; j++) {
int h = get(i, len-j+1, len);
int p = h;//p为脑补前缀v的hash值
if(!ump[p]) continue;
int t1 = 1, t2 = 1;
for(int k = 2;k*j <= len; k++) {
int tmp = get(i, len-k*j+1, len);//实际后缀w的hash值
p = ((LL)p*ax[j]%MOD+h)%MOD;//更新v的hash值,相当于把最开始的后缀加一个前面
if(tmp != p) break;//如果两者不相等,说明已经不能走下去
if(ump[p]) t1 = k;//umap中找到了,说明可以更新|v|的长度
t2 = k;
}
if(t1 == t2 && t1 == 1) continue;
if(t1 == t2) t1--;
if(t1*j + t2*j > r1+r2) {
r1 = t1*j;
r2 = t2*j;
}
}
for(int j = 1;j <= len; j++) ump[hh[i][j]]++;
}
printf("%d %d\n", r1, r2);
}
int main() {
ax[0] = 1;
for(int i = 1;i < L; i++) ax[i] = (LL)ax[i-1]*X%MOD;
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 0;i < n; i++) scanf("%s", ch[i]+1);
solve();
}
return 0;
}