来源:牛客网:

题目描述

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