【题解】【CF631D】Messenger

版权声明:本文为博主原创文章,需转载请联系博主。 https://blog.csdn.net/ezoixx130/article/details/79824155

我们定义S[i]代表S的第i块,T[i]代表T的第i块。

同时S[l..r]代表S的l到r位,并且,T[l..r]代表T的l到r位。

若T是S的字串,则S[l + 1..r - 1]=T[2..m - 1]且S[l].l = T[1].l且S[l].c ≥ T[1].c且S[r].l = T[m].l且S[r].c ≥ T[m].c.

所以我们只需要找到T[2..m-1]SS 中出现的地方,然后再检查一下边界,这可以用KMP算法解决。

另外:

  1. M=1M=2的情况可以特殊处理

  2. 若相邻的两块所含的字符相同,则可以合并

  3. 答案需要储存在long long中

Time: O(n+m)

Memory: O(n+m)

代码:

#include <bits/stdc++.h>
using namespace std;

#define ll long long

struct dat{
	ll l;
	char c;
	friend bool operator<=(const dat &d1,const dat &d2)
	{
		return (d1.c==d2.c) && (d1.l<=d2.l);
	}
	friend bool operator==(const dat &d1,const dat &d2)
	{
		return (d1.c==d2.c) && (d1.l==d2.l);
	}
}a[200001],b[200001];

int f[200001],n,m;

void cp(dat *a,int &n)
{
	int m=0;
	for(int i=0;i<n;++i)
	{
		if(m==0 || a[m-1].c!=a[i].c)
			a[m++]=a[i];
		else
			a[m-1].l+=a[i].l;
	}
	n=m;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;++i)scanf("%lld-%c",&a[i].l,&a[i].c);
	for(int i=0;i<m;++i)scanf("%lld-%c",&b[i].l,&b[i].c);
	cp(a,n);
	cp(b,m);
	ll ans=0;
	switch(m) {
		case 1:{
			for(int i=0;i<n;++i)
            if (b[0]<=a[i])
                ans+=a[i].l-b[0].l+1;
			break;
		}
		case 2:{
			for(int i=0;i<n-1;++i)
            if (b[0]<=a[i]&&b[1]<=a[i + 1])
                ++ans;
			break;
		}
		default:{
			f[1]=0;
        	for(int i=2;i<m-1;i++) {
	            int j=f[i-1];
	            while(j>0&&!(b[j+1]==b[i]))
	                j=f[j];
	            if (b[j+1]==b[i])
	                j++;
	            f[i]=j;
	        }
	        for(int i=1,j=0;i<n-1;i++){
	            while (j>0 && !(b[j+1]==a[i]))
	                j=f[j];
	            if(b[j+1]==a[i])
	                j++;
	            if(j==m-2)
				{
	                if(b[0]<=a[i-j] && b[j+1]<=a[i+1])
	                    ans++;
	                j=f[j];
	            }
	        }
		}
	}
	printf("%lld\n",ans);
}


猜你喜欢

转载自blog.csdn.net/ezoixx130/article/details/79824155