拼凑 牛客练习赛70
题目描述
牛牛还是很喜欢字符串"puleyaknoi"。
牛牛有T个超长超长的字符串,不过这次他更懒了,他希望直接在字符串中看见他喜欢的字符串。
如果一个子串中含有一个子序列是”puleyaknoi“,那么他就把这个子串称作好的子串。
牛牛是个懒人,他不喜欢看太长的子串,那样他会觉着眼镜很累。
你能帮他求出对于每个字符串,最短的好的子串的长度是多少吗?
如果没有,请输出-1。
输入描述:
第一行一个T表示数据组数
接下来T行每行一个字符串(保证字符串只含小写字母)
输出描述:
共T行每行一个答案
示例1
输入
复制
3
sxpuleyaaknoip
pionkaayelupxs
yydspwuwlwewywawkwnwowiw
输出
复制
11
-1
19
备注:
T<=10,字符串长度不超过10^5T≤10,字符串长度不超过10
5
题解:
我一开始想的是在A题的基础上改,A题是打乱顺序,B题是找子串,子串其实就是相对位置已经固定,只是不一定靠着而已
我用A题作为模板改完后,意识到复杂度过高,肯定会TEL,决定换一个方式
用序列自动机来做,序列自动机专门来求子串问题,典型的空间换时间,
记录每个位置往后最近的字母的位置
然后从p才是出发匹配我们所需要的子串
int nxt[maxn][30];
int now[30];
char s[maxn];
void init()
{
//序列自动机预处理
memset(now, -1, sizeof now); //mow_i表示第i个字母在原串中从后向前最晚出现的位置
int len = strlen(s);
--len;
for(int i = len; ~i; --i) //处理每一个字符
{
for(int j = 0; j < 26; ++j) //找出第i个字符后面的26个字母最早出现的字符的位置
nxt[i][j] = now[j];
now[s[i] - 'a'] = i; //用当前字符更新当前字符在原串中从后向前最晚出现的位置
}
}
查找时这样
now = nxt[now][mark[pos++] - ‘a’];
从前向后找
代码:
#include<bits/stdc++.h>
const int INF=1e8+9;
using namespace std;
string s="puleyaknoi";
const int maxn=1e5+9;
int nxt[100009][37];
int now[maxn];
int main()
{
int t;
cin>>t;
while(t--)
{
string a;
cin>>a;
for (int i = 0; i < 26; i++) nxt[a.length()][i] = -1;
for(int i=a.length()-1;i>=0;i--)
{
for(int j=0;j<26;j++)nxt[i][j]=nxt[i+1][j];
int w=a[i]-'a';
nxt[i][w]=i;
}
int ann=INF;
for(int i=0;i<a.length();i++){
if(a[i]==s[0])
{
int time=nxt[i][s[0]-'a'];
int pos=1;
while(time!=-1&&pos<10){
time=nxt[time][s[pos++]-'a'];
}
if(time!=-1&&pos==10)ann=min(ann,time-i+1);
}
}
if(ann==INF)cout<<-1<<endl;
else cout<<ann<<endl;
}
return 0;
}