牛客周赛56 F.不是烤串故事

原题链接:F-不是烤串故事

题意:多测,每组测试给出二个字符串s和v,定义下标从1开始,可以选取字符串s的前i个字符 s1s2⋯si,然后将这一部分反转后与剩余部分拼接,求最长的公共前缀长度和可以让公共前缀最长时的反转下标。

思路:Z函数+dp。Z函数的作用是预处理出s的任意长度的前缀反转之后和v的最长公共子串长度,dp数组的作用是对于不反转的部分求最长公共子串长度。

对于s串来说,如果我们反转了一部分,那么这个时候最长公共前缀是有三种可能,第一种可能是在反转的那一部分里面,第二种是在反转和不反转的交界处,第三种是在不反转的位置。

第一种,只需要让最大值和当前位置的Z函数比较。

第二种和第三种,用最大值和当前位置的Z函数和dp数组的和来比较。

//冷静,冷静,冷静
//调不出来就重构
//#pragma GCC optimize(2)
//#pragma GCC optimize("O3")
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pii;
const int N=1e7+10,mod=1000000007;
ll z[N];
void getz(string s)//z函数板子 
{
	s=' '+s;
	z[1]=s.size()-1;
	for(int i=2,l,r=0;i<s.size();i++)
	{
		if(i<=r)z[i]=min(z[i-l+1],r-i+1ll);
        else z[i]=0;
		while(s[1+z[i]]==s[i+z[i]])z[i]++;
		if(i+z[i]-1>=r)
		{
			r=i+z[i]-1;
			l=i;
		}
	}
}
void Jiuyuan()
{
	ll n;string s,v;cin>>n>>s>>v;
	reverse(s.begin(),s.end());
	getz(v+s);//预处理z函数 
	reverse(s.begin(),s.end());
    vector<ll> f(n+10);
	for(int i=n;i;i--)//dp 
	{
		if(s[i-1]==v[i-1])
		{
			f[i]=f[i+1]+1;
		}
		else f[i]=0;
	}
	ll max1=0,id=1;
	for(int i=1;i<=n;i++)
	{
		ll len=z[2*n-i+1];
		/*
		如果n是3,那么就是1 2 3 4 5 6
		反转1的位置,在z函数里面其实是6,所以1-6,2-5,3-4,肉眼观察可知取2*n-i+1 
		*/
		if(len==i)//如果最长的位置不在反转内部 
		{
			len+=f[i+1];
		}
		if(len>max1)
		{
			max1=len;
			id=i;
		}
	}
	cout<<max1<<' '<<id<<endl;
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	ll T=1;
	cin>>T;
	while(T--)
	{
		Jiuyuan();
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值