CodeForces - 1200E Compress Words (KMP)

题目链接:点击这里

题目大意:
给定 n n n 个字符串,求 n n n 个字符串按序拼接好后的字符串,拼接规则为找到最大的 i ( i ≥ 0 ) i(i\ge 0) i(i0) ,满足第一个单词的长度为ii的后缀和第二个单词长度为 i i i 的前缀相等,然后把第二个单词第ii位以后的部分接到第一个单词后面。输出最后那个单词

题目分析:
最长公共前后缀不难想到 K M P KMP KMP ,我们发现如果想合并 a a a 串和 b b b 串,只需要构造一个 b , a b,a b,a 的串即可,对这个串求一下 n e x t next next 数组,然后最后一位的 n e x t next next 数组值就是最长公共前后缀的长度,然后模拟合并即可。
但是事情似乎没有那么简单,我们发现,如果直接这么做是会超时,因为我如果一开始的串很长,后面的串又很短就会发现这个方法的复杂度是 O ( n 2 ) O(n^2) O(n2) 的。考虑如何优化,因为我们对很多不必要的位置跑了求 n e x t next next ,所以我们限定一下原串与新串合并时合并的范围就可以了。
这时你就会发现不超时了,但 WA \text{WA} WA 了,这是因为比如合并 101 101 101 010 010 010 这两个串时,新组合的串为 010101 010101 010101 最后一位的 n e x t next next 数组值大于单个串长了;这时我们只需要将两个串中间插入一个不可能存在的字符串即可将两个串阻断,防止这种情况发生
注意好了上述问题,这道题就可以顺利 a c ac ac

具体细节见代码:

// Problem: Compress Words
// Contest: Virtual Judge - CodeForces
// URL: https://vjudge.net/problem/CodeForces-1200E
// Memory Limit: 262 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
//#define int  ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 1e6+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,nxt[maxn];
string s,t;
void get_next(string s)
{
	
	int n = s.size();
	s = " "+s;
	for(int i = 2,j = 0;i <= n;i++)
	{
		while(j && s[i]!=s[j+1]) j = nxt[j];
		if(s[i] == s[j+1]) j++;
		nxt[i] = j;
	}
}
int main()
{	
	n = read();
	cin>>s;
	for(int i = 2;i <= n;i++)
	{
		cin>>t;
		int len = min(t.size(),s.size());
		string ss = t+"###"+s.substr(s.size()-len);
		get_next(ss);
		for(int j = nxt[ss.size()];j < t.size();j++) s += t[j];
	}
	cout<<s<<endl;
	return 0;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值