这题就是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);
}