题目描述
牛牛还是很喜欢字符串"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; }