bzoj3796

就是kmp+二分+后缀数组

后缀数组注意中间插一个没有出现的字母,然后从1开始,我是从0的导致常数大了一点点。

kmp的时候标记起点,然后注意从起点开始只有长度达到了strlen(s3)以上的才会包含s3

这题的感觉就是在后缀数组中有相同前缀的一定是紧挨在一起的。

还有注意一下二分技巧:

求最大值从(l+r)/2+1查找 求最小值从(l+r)/2-1查找,最大值最小序更新l,最大值最大序更新r,其他都差不多了。

貌似后缀自动机也阔以做,等我以后再补。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
char str1[200020], str3[20020];
int buc[200020], rank1[200020], ran2pos[200020],sa[200020],lcp[200020],nextt[200020],num[200020];
void getsa(char *s, int len)
{
	int m = len > 400 ? len : 400;
	for (int i = len; i <= 2 * len; i++)ran2pos[i] = -1, rank1[i] = -1;
	for (int i = 0; i < m; i++)buc[i] = 0;
	for (int i = 0; i < len; i++)buc[rank1[i] = s[i]]++;
	for (int i = 1; i < m; i++)buc[i] += buc[i - 1];
	for (int i = len - 1; i >= 0; i--)sa[--buc[rank1[i]]] = i;
	for (int k = 1; k <= len; k*=2)
	{
		int p = 0;
		for (int i = len - 1; i >= len-k; i--)ran2pos[p++] = i;
		for (int i = 0; i < len; i++)if (sa[i]>k - 1)ran2pos[p++] = sa[i] - k;
		for (int i = 0; i < m; i++)buc[i] = 0;
		for (int i = 0; i < p; i++)buc[rank1[ran2pos[i]]]++;
		for (int i = 1; i < m; i++)buc[i] += buc[i - 1];
		for (int i = p - 1; i >= 0; i--)sa[--buc[rank1[ran2pos[i]]]] = ran2pos[i];
		swap(rank1, ran2pos);
		int r = 1; rank1[sa[0]] = 0;
		for (int i = 1; i < len; i++)
		{
			if (ran2pos[sa[i]] == ran2pos[sa[i - 1]] && ran2pos[sa[i] + k] == ran2pos[sa[i - 1] + k])
				rank1[sa[i]] = r - 1;
			else
				rank1[sa[i]] = r++;
		}
		if (r>= len)break;
		m = r;
	}
	int h = 0;
	for (int i = 0; i < len; i++)rank1[sa[i]] = i;
	for (int i = 0; i < len; i++)
	{
		if (rank1[i] == len - 1)continue;
		int now = sa[rank1[i] + 1];
		if (h > 0)h--;
		while (s[now + h] == s[i + h])h++;
		lcp[rank1[i]] = h;
	}
}
void getnext(char *s, int len)
{
	int now = -1; nextt[0] = -1;
	for (int i = 1; i < len; i++)
	{
		while (now!=-1&&s[i] != s[now + 1])now = nextt[now];
		if (s[now + 1] == s[i])nextt[i] = now + 1; else nextt[i] = -1;
		now = nextt[i];
	}

}
void kmp(char *s, int lens,char* model,int lenm)
{
	int now = -1;
	for (int i = 0; i < lens; i++)
	{
			while (now != -1 && s[i] != model[now + 1])now = nextt[now];
			if (s[i] == model[now + 1])now++;
			if (now == lenm - 1)num[i - lenm + 1]++;
	}
	for (int i = 1; i < lens; i++)num[i] += num[i - 1];
}
bool check(int len1,int lenth,int *lcp,int strlenth,int modellenth)
{
	for (int i = 0; i < strlenth-1; i++)//每个后缀对应一个级别,这里i枚举的是级别
	{
		if ((sa[i] > len1&&sa[i + 1] > len1) || (sa[i] <len1&&sa[i + 1] < len1))continue;
		int before, after;
		if (sa[i] > len1&&sa[i + 1] < len1)after = sa[i], before = sa[i + 1]; else after = sa[i + 1], before = sa[i];
		int templen = min(lcp[i], lenth);
		if (templen>= lenth)//这还要注意我们是两个串合在一起的
		{
			if (templen < modellenth)return true;
			int start = before; int end = before+templen-1;
				int newend = end - modellenth+1;//这里主要是看那段有没有model的起点如果有就不行,后面的位置就算是有model的起点也是不存在的。
				if (start-1>= 0)
				{
					if (num[newend] - num[start - 1] == 0)return true;
				}
					else
						if (num[newend] == 0)return true;
		}
	}
	return false;
}
int main()
{
	//int k = 'A';
	//cout << k << endl;
	scanf("%s", str1);
	int len1 = strlen(str1);
	str1[len1] = 'a' - 1;
	len1++;
	scanf("%s" ,str1 + len1);
	int alllen = strlen(str1);
	scanf("%s", str3);
	int all3len = strlen(str3);
	getsa(str1, alllen);
	getnext(str3, all3len);
	kmp(str1, alllen, str3, all3len);
	int l = 0; int r = alllen;
	while (l < r)
	{
		int mid = (l + r) / 2 + 1;//因为要求最大值所以先考虑大的。
		if (check(len1, mid, lcp, alllen, all3len))
			l = mid;
		else
			r = mid - 1;
	}
	printf("%d\n", l);
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值