1. 程式人生 > >HihoCoder1466-後綴自動機六·重復旋律9

HihoCoder1466-後綴自動機六·重復旋律9

\n 字符集 pri color bit 預處理 set scan 所有

小Hi平時的一大興趣愛好就是演奏鋼琴。我們知道一段音樂旋律可以被表示為一段字符構成的字符串。

現在小Hi已經不滿足於單單演奏了!他通過向一位造詣很高的前輩請教,通過幾周時間學習了創作鋼琴曲的基本理論,並開始對曲目做改編或者原創。兩個月後,小Hi決定和前輩進行一場創作曲目的較量!

規則是這樣的,有兩部已知經典的作品,我們稱之為A和B。經典之所以成為經典,必有其經典之處。

剛開始,紙上會有一段A的旋律和一段B的旋律。兩人較量的方式是輪流操作,每次操作可以選擇在紙上其中一段旋律的末尾添加一個音符,並且要求添加完後的旋律依然是所在作品的旋律(也就是A或B的一個子串)。誰詞窮了(無法進行操作)就輸了。

小Hi和前輩都足夠聰明,但是小Hi還是太年輕,前輩打算教訓他一頓。前輩表示會和小Hi進行K次較量,只要小Hi贏了哪怕一次就算小Hi獲得最終勝利。但是前提是開始紙上的兩段旋律需要他定。小Hi欣然同意,並且表示每次較量都讓前輩先操作。

前輩老謀深算,顯然是有備而來。他已經洞悉了所有先手必勝的初始(兩段)旋律。第i天前輩會挑選字典序第i小的初始(兩段)旋律來和小Hi較量。那麽問題來了,作為吃瓜群眾的你想知道,最後一天即第K天,前輩會定哪兩個旋律呢?

初始時兩段旋律的字典序比較方式是先比較前一個旋律字典序,一樣大則比較後一旋律的字典序。

輸入

第一行包含一個正整數,K。K<=10^18

第二行包含一個非空的僅有小寫字母構成的字符串,表示作品A。|A|<=10^5

第三行包含一個非空的僅有小寫字母構成的字符串,表示作品B。|B|<=10^5

輸出

Sample Input

5
ab
cd

Sample Output

a
cd

輸出共兩行,每行一個字符串(可能為空),表示答案。

如果無解則只輸出一行"NO"。

題解:

後綴自動機的next指針DAG圖上求SG 函數值DFS找字典序第K小先手必勝子串對 ;

題目大意:給定兩個字符串A,B ,現在要求分別用A,B 的兩個子串X,Y 進行遊戲,問字典序第K KK小的兩個先手必勝的X,Y 是哪兩個.其中X,Y的字典序比較方式為先比較第一個串的字典序,後比較第二個串的字典序.

遊戲規則為,先手開始遊戲,兩人輪流加字符,先無法操作的人輸.其中每一輪操作的規則為,任選X,Y 中的一個,往後面加一個字符使得X 還是A的子串,Y 還是B的子串.

考慮直接對A,B 都建立SAM,由於是要求它們的子串勝負,所以考慮對於SAM上的每個狀態求出它的勝負,利用SG函數即可.整個遊戲的勝負可以通過SG定理來合並.

值得註意的是,很容易發現SAM上任意一個狀態的sg值都不會超過字符集大小,因為SAM上每個狀態最多連字符集大小條出邊.

得到了每一個狀態的勝負之後,我們考慮在一個SAM上如何找到第k 小的必勝子串,這個問題類似於第k小子串問題,只是我們預處理每一個狀態開始的所有子串的時候要忽略必敗態,也就是忽略sg=0 時候的情況.兩個串的時候,我們可以考慮先確定答案在第一個SAM上的位置,再確定第二個.不過在第一個SAM上跑的時候,要直接減掉可以與正在遍歷的狀態組成必勝態的子串數量,也就是說sg=? 0 ? ,這個時候我們就需要記錄sg為每一個數時的子串數量了.

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define csiz 27
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
char s[maxn],ans1[maxn<<1],ans2[maxn<<1];
int tmp;
ll k;

struct SAM{
    int fa[maxn<<1],nxt[maxn<<1][26],l[maxn<<1];
    int last,ct,sg[maxn<<1],flag[maxn<<1][csiz];
    ll sum[maxn<<1],cnt[maxn<<1][csiz];

    void Init()
    {
        last=ct=1;
        memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        memset(nxt[1],0,sizeof(nxt[1]));
        fa[1]=l[1]=0;
    }
    
    int NewNode()
    {
        ct++;
        memset(nxt[ct],0,sizeof(nxt[ct]));
        fa[ct]=l[ct]=0;
        return ct;
    }
    
    void Insert(int ch)
    {
        int p=last,np=NewNode();
        last=np; l[np]=l[p]+1;
        while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p];
        if(!p) fa[np]=1;
        else
        {
            int q=nxt[p][ch];
            if(l[q]==l[p]+1) fa[np]=q;
            else
            {
                int nq=NewNode();
                memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
                fa[nq]=fa[q];
                l[nq]=l[p]+1;
                fa[np]=fa[q]=nq;
                while(nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p];
            }
        }
    }
    
    int GetSG(int k)
    {
          if(sg[k]^-1) return sg[k];
          for(int i=0;i<=26;++i) flag[k][i]=0;
          for(int i=0;i<26;++i)
            if(nxt[k][i])
        {
            flag[k][GetSG(nxt[k][i])]=1;
            for(int j=0;j<=26;++j)
                  cnt[k][j]+=cnt[nxt[k][i]][j]; 
          }
          int i=0;
          while(flag[k][i]) ++i;
          sg[k]=i;
        ++cnt[k][sg[k]];
        for(int i=0;i<=26;++i) sum[k]+=cnt[k][i];
        return sg[k];
      }
      
    void Build(char*ss)
    {
        Init();
        int len=strlen(ss);
        for(int i=0;i<len;++i) Insert(ss[i]-a);
        for(int i=1;i<=ct;++i) sg[i]=-1;
        GetSG(1);
    }
}sa,sb;

ll calc(int a,int b)
{
      ll sum=0;
      for(int i=0;i<=26;++i)
        sum+=sa.cnt[a][i]*(sb.sum[b]-sb.cnt[b][i]);
      return sum;
}

int dfs1(int x,int pos)
{
      ll sum1=sb.sum[1]-sb.cnt[1][sa.sg[x]],sum2; 
      if(sum1>=k) return x;
      else k-=sum1;
      for(int i=0;i<26;++i)
      {
        if(sa.nxt[x][i])
        {
              sum2=calc(sa.nxt[x][i],1);
              if(sum2<k) k-=sum2;
              else 
              {
                  ans1[pos]=a+i;
                  return dfs1(sa.nxt[x][i],pos+1);
              }
        }
    }
      return -1;
}

int dfs2(int x,int pos)
{
    k-=sa.sg[tmp]!=sb.sg[x];
    if(k==0) return x;
    ll sum;
    for(int i=0;i<26;++i)
        {
        if(sb.nxt[x][i])
        {
              sum=sb.sum[sb.nxt[x][i]]-sb.cnt[sb.nxt[x][i]][sa.sg[tmp]];
              if(sum<k) k-=sum;
              else 
              {
                ans2[pos]=a+i;
                return dfs2(sb.nxt[x][i],pos+1);
              }
        }
    }
       return -1;
}

int main()
{
    scanf("%lld",&k);
    scanf("%s",s);
    sa.Build(s);
    scanf("%s",s);
    sb.Build(s);
    tmp=dfs1(1,1);
    if(tmp^-1) dfs2(1,1);
    if(tmp==-1) puts("NO");
    else printf("%s\n%s\n",ans1+1,ans2+1);
    return 0;
}
View Code

HihoCoder1466-後綴自動機六·重復旋律9