[agc007f] Shik and Copying String 模拟神题

Description

​ “全”在十分愉快打工,第0天,给了他一个仅有小写字母构成的长度为N的字符串S0,在之后的第i天里,“全”的工作是将Si−1复制一份到一个新的字符串Si中,在接下来的描述中,我们用Si[j]表示串Si中的第j个字符
然而“全”十分不熟练,在复制的时候他容易出错。比如说在复制Si−1[j]到Si[j]的时候有时他会把Si[j]上的字符写成Si[j−1]Si[j−1],而不是本应被写上去的Si−1[j],更具体一点就是新串中的字符Si[j]可能等于Si[j−1]或者Si−1[j]
这让“全”很困扰,现在他给你S0和另一个长度同样为N的字符串T,希望你求出一个最小的i,能满足Si=T,如果不存在这样的i,输出−1

Input

​ 第一行一个正整数N
第二行一个字符串S0
第三行一个字符串T

Output

​ 输出最小的满足条件的i,无解输出−1

Sample Input

#Sample1
5
abcde
aaacc

#Sample2
5
abcde
abcde

#Sample3
4
acaa
aaca

#Sample4
5
abcde
bbbbb

Sample Output

#Sample1
2

#Sample2
0

#Sample3
2

#Sample4
-1

HINT

数据范围:

​ 对于100%的数据,1<=N<=10^6,全部都是小写字母

样例解释:

​ Sample1:S0=abcde,S1=aaccc,S2=aaacc

Sol

神题。

我们首先可以预处理出来T中每一个字符是从S中的哪一个位置推过来的,记为(c[i])

我们从右向左遍历,如果遇到了连续字母段的尾部则停下,并且设这个位置为i。

接着我们可以把S中一个字符的移动轨迹视为一条折线,显然对于T中一段相同的字母段,是用同一条折线转移过来的。而我们的答案就是折线的横边个数,因为折线的纵线没有意义,而横线就是每次染到的位置。

我们考虑使用双端队列维护折线的每个横线的右端点,显然每时每刻队列中只会描述一个折线,而对于不同的折线,显然我们发现前面的折线先多拐一个弯(避开上一条折线),然后贴着后面的走,直到碰到某个字母段的最后一个字母停止,后面的不继承了。

如果出现了(s[i]==t[i])的情况,那么我们把队列清空,把接下来要处理的折线放进队列,因为被(s[i]==t[i]) 隔开的东西是互不影响的((c[i])和i连线是不可能相交的)。

上幅图形象解释一下:

ggg

我们的目标是让一个折线到达某个字母连续段的最后一位,如果继承的折线已经超过了这个位置(i),那么后面的折线不会再有意义,直接舍去即可,舍去完之后把当前折线的终点(就是i)再pushback进去就是当前完整的折线。而且我们发现每个折线继承部分都是上一个折线的某一部分,但是由于上一个折线的作用,折线又不能相交,所以我们还是需要把新的折线多拐一次,即上面红线部分,在遇到右边折线的时候要拐一个弯,这个pushfront即可。(中间部分完全认为是原来折线+偏移量)

同时我们维护一个全局变量lz,表示遇到了多少个折线,作用就是我们可以快速计算某个折线由于前面折线而往左偏移了多少。因为我们发现每次继承的拐点正好左移一位。

每条折线的长度取max就是答案。

Code

代码其实还不到1K。

#include <bits/stdc++.h>
using namespace std;
char a[1000005],b[1000005];int n,ok,c[1000005],lz,ans,cnt;vector<int>v[30];deque<int>q;
int main()
{
    scanf("%d%s%s",&n,a+1,b+1);c[n+1]=n+1;
    for(int i=1;i<=n;i++) ok+=a[i]==b[i];
    if(ok==n) return puts("0"),0;
    if(n==1) return puts("-1"),0;
    for(int i=1;i<=n;i++) v[a[i]-'a'].push_back(i);
    for(int i=n;i;i--)
	{
	    if(b[i]==b[i+1]&&i>=c[i+1]){c[i]=c[i+1];continue;}
	    else
		{
		    while(!v[b[i]-'a'].empty()&&v[b[i]-'a'].back()>=c[i+1])
			{
			    v[b[i]-'a'].pop_back();
			    if(v[b[i]-'a'].empty()) return puts("-1"),0;
			}
		    c[i]=v[b[i]-'a'].back();
		    if(i>=c[i+1])
			{
			    lz--;q.push_front(c[i+1]-lz-1);
			    while(q.back()+lz>=i) q.pop_back();
			    q.push_back(i-lz);
			}
		    else{while(!q.empty()) q.pop_back();if(c[i]<i) q.push_back(i-lz);}
		    ans=max(ans,(int)q.size());
		}
	}
    cout<<ans<<endl;
}
原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9520495.html