题目大意:有 n 个单词,按顺序把 n 个单词拼起来成一整个单词,拼起来的时候前一个单词的后缀 和后一个单词的前缀的最长相同部分要省略掉一个,例如:apple please 拼起来是 applese
所有单词总长不超过
1
0
6
10^6
106
题解:用字符串hash可以 O ( n ) O(n) O(n)处理, O ( 1 ) O(1) O(1)比较。因为每个串只会被扫一遍,可以直接暴力,预处理 O ( n ) O(n) O(n),暴力 O ( n ) O(n) O(n)查找最长相同部分。
由于字符串长达到了
1
0
6
10^6
106,单模hash 已经搞不了,要用更安全的双 hash,取 2 个较大的模数 mod1 和 mod2,以及一个素数 p (p 不用太大,单模的 p 也不用太大,模数一定要大)
(这题就作为字符串hash入门了)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 3e6 + 10;
#define pll pair<long long,long long>
ll p = 201326611;
ll h = 402653189;
ll pw[maxn],hw[maxn];
ll hash1[maxn];
ll hash2[maxn];
ll tmp1[maxn],tmp2[maxn];
char s[maxn],t[maxn];
int n;
pll Hash(int l,int r,ll hash1[],ll hash2[]) {
return pll((hash1[r] - hash1[l - 1] * hw[r - l + 1] % h + h) % h,(hash2[r] - hash2[l - 1] * pw[r - l + 1] % p + p) % p);
}
int main() {
hw[0] = pw[0] = 1;
for(int i = 1; i < maxn; i++) {
hw[i] = hw[i - 1] * 131 % h;
pw[i] = pw[i - 1] * 131 % p;
}
scanf("%d",&n);
int tot = 1,lst = 1;
hash1[0] = hash2[0] = 0;
for(int i = 1; i <= n; i++) {
if(i == 1) {
scanf("%s",s + 1);
tot = strlen(s + 1);
continue;
}
scanf("%s",t + 1);
int len = strlen(t + 1);
for(int j = lst; j <= tot; j++) {
hash1[j] = (hash1[j - 1] * 131 + 1ll * s[j]) % h;
hash2[j] = (hash2[j - 1] * 131 + 1ll * s[j]) % p;
}
lst = tot;
for(int j = 1; j <= len; j++) {
tmp1[j] = (tmp1[j - 1] * 131 + 1ll * t[j]) % h;
tmp2[j] = (tmp2[j - 1] * 131 + 1ll * t[j]) % p;
}
int pos = 0;
for(int j = 1; j <= len && j <= tot; j++)
if(Hash(1,j,tmp1,tmp2) == Hash(tot - j + 1,tot,hash1,hash2))
pos = j;
for(int j = 1 + pos; j <= len; j++)
s[++tot] = t[j];
}
s[++tot] = 0;
printf("%s\n",s + 1);
return 0;
}