题目链接

https://ac.nowcoder.com/acm/contest/7509/B

解题思路

我的思路:
pos记录属于puleyaknoi中字符的位置,pos_of_p_in_pos记录p在pos中的位置。
从pos_of_p_in_pos数组的最后一个p开始往后找字符串,按顺序puleyaknoi中的字符,若能找全,刷新ans。
大佬思路:
核心是用v[i]表示puleyaknoi中所有第i个字符的位置,是从小到大排序的,因为是从小到大push的。
枚举p的位置,作为区间左端点,字符串最右端作为右端点,check参数为i和二分的区间长度。
长度可行刷新ans,同时缩短区间。

我的AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
char s[15]=".puleyaknoi";
int FindIDX(char ch){
    for(int i=1;i<=10;i++) if(ch==s[i]) return i;
    return 0;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        string  str;
        cin>>str;
        int len=str.length();
        str='.'+str;

        int pos[N],pos_of_p_in_pos[N];
        int num=0,nump=0;
        for(int i=1;i<=len;i++){
            int tmp=FindIDX(str[i]);
            if(tmp) pos[++num]=i;
            if(tmp==1) pos_of_p_in_pos[++nump]=num;
        }

        int ans=N;
        for(int i=nump;i>=1;i--){
            int k=1,j=pos_of_p_in_pos[i];
            for(;j<=num&&k<=10;j++){
                if(str[pos[j]]==s[k]) k++;
            }
            if(k==11) ans=min(ans,pos[j-1]-pos[pos_of_p_in_pos[i]]+1);
        }
        if(ans>len) cout<<-1<<endl;
        else cout<<ans<<endl;
    /*    
        cout<<"________________________________"<<endl;
        for(int i=1;i<=num;i++) cout<<pos[i]<<' ';
        cout<<endl;
        for(int i=1;i<=nump;i++) cout<<pos_of_p_in_pos[i]<<' ';
        cout<<endl;
        cout<<"________________________________"<<endl;
    */
    }
} 

大佬AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
vector<int> v[N];
int IS(char ch){
    if(ch=='p') return 1;
    else if(ch=='u') return 2;
    else if(ch=='l') return 3;
    else if(ch=='e') return 4;
    else if(ch=='y') return 5;
    else if(ch=='a') return 6;
    else if(ch=='k') return 7;
    else if(ch=='n') return 8;
    else if(ch=='o') return 9;
    else if(ch=='i') return 10;
    return 0;
}
bool check(int p,int len){
    int tmp=p;
    for(int i=2;i<=10;i++){
        p=*lower_bound(v[i].begin(),v[i].end(),p);//不可以用upper_bound,因为假如p=N,那么p在下一次循环中的upper_bound中会被赋值成v中未知域的随机值。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
vector<int> v[N];
int IS(char ch){
    if(ch=='p') return 1;
    else if(ch=='u') return 2;
    else if(ch=='l') return 3;
    else if(ch=='e') return 4;
    else if(ch=='y') return 5;
    else if(ch=='a') return 6;
    else if(ch=='k') return 7;
    else if(ch=='n') return 8;
    else if(ch=='o') return 9;
    else if(ch=='i') return 10;
    return 0;
}
int GetLenth(int p,int len){
    int tmp=p;
    for(int i=2;i<=10;i++){
        p=*lower_bound(v[i].begin(),v[i].end(),p);
    } 
    if(p>tmp+len-1) return 0;
    else return p-tmp+1; 
}
int main(){
    int t;
    cin>>t;
    while(t--){
        string str;
        for(int i=0;i<=10;i++) v[i].clear();
        cin>>str;
        int len=str.length();
        str='.'+str;
        for(int i=1;i<=len;i++){
            v[IS(str[i])].push_back(i);
        }
        for(int i=1;i<=10;i++) v[i].push_back(N);

        int ans=N;
        for(int i=1;i<=len;i++){
            if(str[i]=='p'){
                int tmp=GetLenth(i,len-i+1);
                if(tmp) ans=min(ans,tmp);
            }
        }
        if(ans>=N) cout<<-1<<endl;
        else cout<<ans<<endl;
    }
}
    } 
    if(p>tmp+len-1) return false;
    else return true; 
}
int main(){
    int t;
    cin>>t;
    while(t--){
        string str;
        for(int i=0;i<=10;i++) v[i].clear();
        cin>>str;
        int len=str.length();
        str='.'+str;
        for(int i=1;i<=len;i++){
            v[IS(str[i])].push_back(i);
        }
        for(int i=1;i<=10;i++) v[i].push_back(N);

        int ans=N;
        for(int i=1;i<=len;i++){
            if(str[i]=='p'){
                int l=10,r=len-i+1;
                while(l<=r){//二分
                    int m=(l+r)>>1;
                    if(check(i,m)) r=m-1,ans=min(ans,m);
                    else l=m+1;
                }
            }
        }
        if(ans>=N) cout<<-1<<endl;
        else cout<<ans<<endl;
    }
}

改编大佬代码后

我发现其实二分操作是多余的,只要用容器存好,利用lower_bound函数求出的就是最短的。
GetLenth参数为左端点为i,右端点始终为字符串右端点。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
vector<int> v[N];
int IS(char ch){
    if(ch=='p') return 1;
    else if(ch=='u') return 2;
    else if(ch=='l') return 3;
    else if(ch=='e') return 4;
    else if(ch=='y') return 5;
    else if(ch=='a') return 6;
    else if(ch=='k') return 7;
    else if(ch=='n') return 8;
    else if(ch=='o') return 9;
    else if(ch=='i') return 10;
    return 0;
}
int GetLenth(int l,int r){
    int tmp=l;
    for(int i=2;i<=10;i++){
        l=*lower_bound(v[i].begin(),v[i].end(),l);
    } 
    if(l>r) return 0;
    else return l-tmp+1; 
}
int main(){
    int t;
    cin>>t;
    while(t--){
        string str;
        for(int i=0;i<=10;i++) v[i].clear();
        cin>>str;
        int len=str.length();
        str='.'+str;
        for(int i=1;i<=len;i++){
            v[IS(str[i])].push_back(i);
        }
        for(int i=1;i<=10;i++) v[i].push_back(N);

        int ans=N;
        for(int i=1;i<=len;i++){
            if(str[i]=='p' && len-i+1>=10){
                int tmp=GetLenth(i,len);
                if(tmp) ans=min(ans,tmp);
            }
        }
        if(ans>=N) cout<<-1<<endl;
        else cout<<ans<<endl;
    }
}