原题链接: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;
}