这道题好难喵!
给猫猫做傻了喵!
借用了蒟蒻果冻01大佬的O(n) 思想,仅仅只是为了让更多人和猫猫一样理解他的思想喵!
猫猫解释时间到!(≧▽≦)
第一步:看看 t 的开头有几个相同的小可爱
设定一个 z 来统计 t 开头有多少个连续相同的字符
int z = 1; while (z + 1 < n && t[z] == t[z + 1]) ++z;
就像在数一群小猫咪,看看它们是不是都穿着同样的衣服。比如 t = "aaab",前三个都是 'a',所以 z = 3。这个信息后面有大用处喵!
第二步:从尾巴开始,数一数 s 和 t 有多少连续相同的位置
也就是数组b的含义啦!
b [ i ]的值代表从 i 开始往后有多少个s所在位和t所在位连续相同的字符数。
for (int i = n - 1; i >= 0; --i) {
if (s[i] == t[i]) b[i] = 1 + b[i+1];
else b[i] = 0;
}
从右往左看,如果当前位置两个字符一样,就记下从这往右有多少个连续一样的。
这样后面如果整个反转部分都匹配上了,就能直接加上这些后续的匹配长度啦!
第三步:递推 a[i] —— 最核心的一步!
a [ i ]的值代表从0到i翻转后,在“翻转范围内”从前往后可以和t匹配的最大长度。注意不是题目中的lcp啦!
if (s[i] != t[0]) { a[i] = 0; continue; }
反转后的第一个字符是 s[i],如果它和 t[0] 不一样,那直接没戏,a[i] = 0。
否则,我们看看前一个状态 a[i-1] 是多少,记作 x。
现在有两种情况:
- 如果 x 比 z 还大:说明之前匹配的长度已经超过了 t 开头重复段的长度
- 那么a[i]就直接等于z。
- 举个例子喵!
- 假如a[4]所在的反转范围内的s为abaa翻转过来是aaba,假设t为aaba。那么此时a[4]是4大于z(2)。
- 在a[5]时所在的s一定是abaaa,翻转过来时aaaba原本的第三位的a必定和b(即第z+1位的不同值冲突)
- 故而a[i]一定等于z。
- 如果 x 小于等于 z :说明 x 没有超过重复段,我们可以尝试继续向后匹配。
- 从 p = x+1 开始,比较 t[p] 和 s[i-p](因为反转后第 p 个字符来自原串的 i-p 位置)。一直比到不匹配或者超过 i 为止,a[i] 就等于这个 p。
- 为什么要从x+1开始呢?
- 之前已经判断了s[i]和t[0]一样了且之前匹配的数(x)比z小,即全是t[0]。那么一定0到x是匹配的喵!
第四步:组合成真正的 LCP
int now = a[i];
if (now == i + 1) {
if (i + 1 < n) now += b[i + 1];
}
如果整个反转部分都匹配了(now == i+1),那么后面可能还能接上原串剩余部分的匹配,而 b[i+1] 正好告诉我们后面能接多长。这样 now 就是真正的 LCP 长度啦!
第五步:找最大值和最小下标
遍历所有 i,记录最大的 now 和最先出现的 i,然后输出(记得 i 要加 1 哦)。
接下来就是代码展示了,代码的注释更清晰喵!
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
int n;cin >> n;//字符串长度
string s,t;cin >> s >> t;//两个字符串
int z=1;//统计t开头有多少个连续相同的字符
while(z+1<n&&t[z]==t[z+1]) z++;
vector<int> a(n,0);//代表从0到i翻转后,
//在“翻转范围内”从前往后可以和t匹配的最大长度
//注意不是题中的lcp
vector<int> b(n,0);//从i开始往后有多少个“连续”相等的字符数
//初始化b
for(int i=n-1;i>=0;i--)
{
if(s[i]==t[i])
{
b[i]=1;//只要该位相等就至少有个1
if(i+1<n) b[i]+=b[i+1];//继承后一位的
}
else b[i]=0;
}
//初始化a
if(s[0]==t[0]) a[0]=1;//初始情况
for(int i=1;i<n;i++)
{
//从0到i翻转
if(s[i]!=t[0]) //如果是s[i]不等于t[0]
continue;//那么反转后的第一个字符就不匹配,直接跳
int x=a[i-1];//继承
//x意为翻转0到i-1时反转部分与t的匹配长度
if(z<x)
{
//之前匹配的长度超过了z(t的前缀重复段)
//新匹配最多只能到重复段末尾
a[i]=z;
}
else
{
//之前已经判断了s[i]和t[0]一样了
//且之前匹配的数(x)比z小,即全是t[0]
//那么一定0到x是匹配的
int p=x+1;
//进行剩下的匹配
while(p<=i&&t[p]==s[i-p]) p++;
a[i]=p;//得到匹配的值
}
}
int mx=0,best_i=-1;
for(int i=0;i<n;i++)
{
int now=a[i];//翻转范围内匹配的数量
if(now==i+1)//如果翻转范围内全匹配了
{
if(i+1<n) now+=b[i+1];//接上后面的
}
if(now>mx)//如果比mx大就更新
{
mx=now;
best_i=i;
}
}
if(mx==0) cout << "0 1\n";
else cout << mx << ' ' << best_i+1 << '\n';
//注意题中索引是从1开始的
}
int main() {
cin.tie(0)->sync_with_stdio(0);
int t;cin >> t;
while(t--) solve();
}
/*
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⡀⣀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡀⢀⣴⣾⣷⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⠿⠛⠛⠉⠉⠉⠉⠉⠉⠛⠻⠿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⡿⠿⠛⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣿⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣀⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⣰⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣶⣄⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣻⣿⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢹⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⠁⠈⢢⡀⠀⠀⠀⢸⡇⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⡟⠒⢦⡀⠀⠀⠀
⠀⠀⣠⣤⣤⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠉⢢⣄⠀⠀⢿⠊⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⡄⠀⢷⠀⠀⠀
⠀⢰⠇⠀⣰⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⡌⣹⠗⠦⣬⣇⠀⠉⢢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡀⢸⡄⠀⠀
⠀⡟⠀⣼⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣆⢹⡀⠀⠀⠀⠉⠁⠀⠀⢀⣀⡁⠀⠀⠉⠳⢴⡆⠀⠀⠀⠀⠀⠀⢹⣧⠈⡇⠀⠀
⠀⡇⠀⠀⢻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣾⠻⠉⠛⠂⠀⠀⠀⠀⠀⠀⠻⠿⣿⣿⣿⣶⣦⡀⠛⣇⠀⠀⠀⠀⠀⣈⣿⠀⡇⠀⠀
⢸⡇⠀⠀⢠⣿⣷⣦⣀⡸⣷⣦⣶⡂⠉⠉⠉⢁⣤⣶⡶⠀⠀⠀⣀⣀⡴⠀⠀⠀⠀⠀⠀⠈⠉⠉⠁⠀⡟⢀⣴⣟⣰⣾⣿⣏⣠⠇⠀⠀
⠈⡇⠀⠀⢸⣿⠁⠉⣿⠛⠛⠃⡇⠀⠀⢠⣶⣿⡿⠛⠁⠀⠀⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⢿⠟⠿⢿⡏⠀⠘⣿⡀⠀⠀⠀
⠀⢷⣀⣀⣿⠇⠀⠀⢿⡇⠀⢀⢱⡀⠀⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⠀⢸⠇⠀⠀⢹⣿⣄⠀⠀
⠀⠀⣉⣿⡏⠀⠀⠀⠀⠀⠀⢸⣇⣳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⣿⠃⠀⠀⠀⠀⠀⠀⣿⠈⢧⠀
⠀⠘⣿⣿⠁⠀⠀⠀⠀⠀⠀⠘⣿⡛⣶⠀⠀⣠⠔⠒⠛⠒⠦⡀⠀⠀⠀⠀⣠⡤⠶⠤⢤⣀⠀⠀⠀⢀⣏⡄⠀⠀⠀⠀⠀⡀⣿⡆⠈⣧
⣠⡾⠛⣿⣿⣧⠀⠀⠀⠀⢸⣿⠾⢿⡿⠀⣰⠃⠀⠀⠀⠀⠀⢹⡄⠀⠀⡼⠁⠀⠀⠀⠀⠈⠙⣦⠀⢸⣿⡇⣾⣣⡀⠀⢰⣿⣿⣿⣤⠾
⡟⠀⠀⠻⣿⡟⢷⡄⣤⡀⠈⣿⡀⣸⠇⠀⠏⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⡇⢀⡀⠀⠀⠀⠀⢀⡟⠀⠀⠋⣿⣿⣿⡇⣠⣿⠿⠛⢷⡀⠀
⠀⠀⠀⠀⣿⣇⣨⣿⣿⣿⣦⣽⣷⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠃⠀⠙⠢⠤⠤⠴⢾⠀⠀⠀⠀⢸⣷⣿⣿⠟⠁⠀⠀⠈⣧⠀
⠀⠀⠀⠀⠈⠉⠉⠁⠈⠉⠈⢉⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⣿⠀
*/

京公网安备 11010502036488号