낙곡P3975[천진성선2015](접미사 자동기DP)

33972 단어 sam거푸집dp

제목 링크


https://www.luogu.com.cn/problem/P3975

문제풀이


이 문제는 매우 고전적이고 중요한데sam의 함곡관이니 반드시 떼어야 한다.
  • 각 점의 endpos 크기를 기록하는 방법은parent 트리에서 아래에서 위로 밀어내는 것이다.시작하는 ispre는 현재 점과 원래 접두사를 포함하지 않은 것을 보십시오. 있으면parent tree가 갈라질 때 잃어버립니다.즉, 현재 노드는 모든 하위 노드의 endpos 집합이 아니며, 집합은 그 접두사의 끝 위치를 더해야만 현재 노드의 endpos 집합이다.
  • endpos 크기의 dp를 구하는 것은 매우 고전적이다.
  • dp수조는 어떤 점에서 출발하여 자동기를 따라 내려가는 서브열이 얼마나 되는지를 나타낸다.각 점의 분자 직렬은 가장자리를 나가는 문자에 자동기의 다음 상태의 문자열을 더해 만든 직렬(dp[nex])이고, 일부는 가장자리 문자가 스스로 형성한 직렬(endpos[nex])이다.(이상 dp는 동일하다고 한다).
  • 이후 트리를 검색하듯 자동기에서 검색한다.아래로 내려갈 때도 일정 수량의 꼬치가 소모될 수 있으니 주의해라.
  • 천진oi의 스타일은 바로 데이터 구조 템플릿 문제이다.코드를 보면 알 수 있다.

  • AC 코드

    #include
    #include
    #include
    using namespace std;
    const int NN=5e5+10;
    struct NODE
    {
        int ch[26];
        int len,fa;
        NODE(){memset(ch,0,sizeof(ch));len=0;}
    }dian[NN<<1];
    int is_pre[NN<<1];//       
    int las=1,tot=1;
    void add(int c)
    {
        int p=las;int np=las=++tot;
        is_pre[np]=1;//endpos[np]={i} where s[i]=c
        dian[np].len=dian[p].len+1;
        for(;p&&!dian[p].ch[c];p=dian[p].fa)dian[p].ch[c]=np;
        if(!p){dian[np].fa=1;}//   case 1
        else
        {
            int q=dian[p].ch[c];
            if(dian[q].len==dian[p].len+1)dian[np].fa=q;//   case 2
            else
            {
                int nq=++tot;dian[nq]=dian[q];//endpos  i 
                dian[nq].len=dian[p].len+1;
                dian[q].fa=dian[np].fa=nq; 
                for(;p&&dian[p].ch[c]==q;p=dian[p].fa)dian[p].ch[c]=nq;//   case 3
            }
        }
    }
    int n;
    vector<int>parentcon[NN<<1];
    char s[NN];
    void sam(){
        las=1,tot=1;
        for(int i=1;i<(n<<1);i++)is_pre[i]=0;
        for(int i=1;i<=n;i++)add(s[i]-'a');
        for(int i=1;i<=tot;i++){
            parentcon[dian[i].fa].push_back(i);
        }
    }
    long long endpos[NN<<1],dp[NN<<1];
    void get_endpos(int cur,int fa){
        int up=parentcon[cur].size();
        endpos[cur]=is_pre[cur];
        for(int i=0;i<up;i++){
            int nex=parentcon[cur][i];
            if(nex!=fa){
                get_endpos(nex,fa);
                endpos[cur]+=endpos[nex];
            }
        }
    }
    void get_num(int cur){//   
        if(dp[cur])return;
        dp[cur]=0;
        for(int i=0;i<26;i++){
            int nex=dian[cur].ch[i];
            if(nex){
                get_num(nex);
                dp[cur]+=dp[nex]+endpos[nex];//dp[nex] i+xxx   ,endpos[nex] i   ,dp    
            }
        }
    }
    void get_num_dif(int cur){//    
        if(dp[cur])return;
        dp[cur]=1;//   
        for(int i=0;i<26;i++){
            int nex=dian[cur].ch[i];
            if(nex){
                get_num_dif(nex);
                dp[cur]+=dp[nex];
            }
        }
    }
    int ans[NN],anslen;
    int t;
    int dfs(int cur,int k,int deep){
        if(k<=0){anslen=deep-1;return k;}
        for(int i=0;i<26;i++){
            int nex=dian[cur].ch[i];
            if(nex){
                int simp=(t?endpos[nex]:1);
                if(dp[nex]+simp>=k){
                    ans[deep]=i;
                    return dfs(nex,k-simp,deep+1);
                    break;
                }
                else{
                    k-=(dp[nex]+simp);
                }
            }
        }
        return k;
    }
    
    int len;
    int main()
    {
    
        scanf("%s",s+1);n=strlen(s+1);
        sam();
        int k;
        scanf("%d%d",&t,&k);
        memset(dp,0,sizeof(dp));
        memset(endpos,0,sizeof(endpos));
        if(t==1){
            get_endpos(1,0);
            get_num(1);
            if(k>dp[1])printf("-1
    "
    ); else{ dfs(1,k,1); for(int i=1;i<=anslen;i++)printf("%c",ans[i]+'a'); printf("
    "
    ); } } else{ get_num_dif(1); for(int i=1;i<=tot;i++)dp[i]--;// if(k>dp[1])printf("-1
    "
    ); else{ dfs(1,k,1); for(int i=1;i<=anslen;i++)printf("%c",ans[i]+'a'); printf("
    "
    ); } } return 0; }

    좋은 웹페이지 즐겨찾기