题目
思路与代码
请看@wawcac 的博客,为本文提供了思路与 代码三
。
做法还挺多的。尽管这样我也没想到。
思路一
暴力哈希判断。其实,你只要发现一点:将 S , T S,T S,T 合并,被压缩的部分只会有 min ( ∣ S ∣ , ∣ T ∣ ) \min(|S|,|T|) min(∣S∣,∣T∣) ,此题就可做了(我一直以为会是 ∣ S ∣ + ∣ T ∣ |S|+|T| ∣S∣+∣T∣ 所以不敢打……)。类似于启发式合并。
代码一
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int x){
if(x > 9) writeint(x/10);
putchar((x%10)^48);
}
# define MB template < class T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }
const int MaxN = 1000005;
const int base1 = 37, Mod1 = 19491001;
const int base2 = 29, Mod2 = 998244353;
# define add1(a,b) ((a)+Mod1+(b))%Mod1
# define add2(a,b) ((a)+Mod2+(b))%Mod2
char s[MaxN], t[MaxN];
void Merge(int &lens,int lent){
int res = 0, b1 = 1, b2 = 1;
auto hs = make_pair(0,0), ht = hs;
for(int i=1; i<=lens and i<=lent; ++i){
hs.first = add1(1ll*b1*s[lens-i],hs.first);
ht.first = add1(1ll*ht.first*base1,t[i-1]);
hs.second = add2(1ll*b2*s[lens-i],hs.second);
ht.second = add2(1ll*ht.second*base2,t[i-1]);
if(hs == ht) res = i;
b1 = 1ll*b1*base1%Mod1, b2 = 1ll*b2*base2%Mod2;
}
for(int i=lens; i<lens+lent-res; ++i)
s[i] = t[i-lens+res];
lens += lent-res;
}
int main(){
int lens = 0;
for(int n=readint(); n; --n)
scanf("%s",t), Merge(lens,strlen(t));
s[lens] = '\0', puts(s);
return 0;
}
思路二
后缀和前缀相同……?那不就是一个 b o r d e r \tt{border} border 吗?
只需要对于字符串 S ′ = T + S S'=T+S S′=T+S 求一次 b o r d e r \tt{border} border 即可。
时间复杂度似乎是 O ( ∣ S ∣ + ∣ T ∣ ) \mathcal O(|S|+|T|) O(∣S∣+∣T∣) 的了?
注意到 b o r d e r \tt{border} border 的长度超过 min ( ∣ S ∣ , ∣ T ∣ ) \min(|S|,|T|) min(∣S∣,∣T∣) 无益,所以只需要把 S S S 的后 ∣ T ∣ |T| ∣T∣ 位拿出来即可。
说来说去,都是启发式合并的思想。
思路三
将 S S S 作为文本串, T T T 作为模式串,跑一次匹配。因为只需要知道最后的匹配,所以只需要截取 S S S 的后 ∣ T ∣ |T| ∣T∣ 位作为文本串。
在这个匹配中,我们要一口气匹配到 S S S 的末尾——相当于求解一个最长的 T T T 的前缀,满足其与 S S S 的后缀匹配。这刚好是这道题要求的东西啊!
代码三
#include<cstdio>
#include<cstring>
#include<algorithm>
const int MAXN=1e6+5;
int n;
char a[MAXN],b[MAXN];
int nxt[MAXN];
int lena,lenb;
int kmp(int st)//传入模式串的匹配起点
{
int i,j;
nxt[0]=-1;
for(i=1,j=-1;i<lenb;i++)
{
while(~j&&b[i]!=b[j+1]) j=nxt[j];
if(b[i]==b[j+1]) j++;
nxt[i]=j;
}
// for(int x=0;x<lenb;x++) printf("%d ",nxt[x]);
// puts(b);
for(i=st,j=-1;i<lena;i++)
{
while(~j&&b[j+1]!=a[i]) j=nxt[j];
if(a[i]==b[j+1]) j++;
if(j==lenb-1) return lenb;
}
return j+1;
}
int main()
{
//freopen("test.in","r",stdin);
scanf("%d%s",&n,a);
lena=strlen(a);
n--;
while(n--)
{
scanf("%s",b);
lenb=strlen(b);
int temp=kmp(std::max(0,lena-lenb));
// printf("%d\n",temp);
for(int i=temp;i<lenb;i++)
a[lena++]=b[i];
}
a[lena]=0;
puts(a);
return 0;
}