[USACO07DEC]Best Cow Line G 字符串hash || 後綴數組

[USACO07DEC]Best Cow Line G

[USACO07DEC]Best Cow Line Gc++


小聲嗶嗶:字符串hash牛逼數組

題意

給出一個字符串,每次能夠從字符串的首尾取出一個字符,放到隊列的尾部,求能夠獲得的最小的字典序是多少?spa

思路1

此時字符串首尾的下標分別爲l,r。code

若是str[l]!=str[r]:取較小的字符串隊列

若是str[l]==str[r]:找到第一個非負整數x,使得str[l+x]!=str[r-x]字符串

​ 若是str[l+x]<str[r-x],那麼此時取str[l],不然取str[r]get

數據範圍是\(1 \leq N \leq 5\times10^5\),若是暴力複雜度比較高博客

若是快速找到x是關鍵,我想了字符串hash,沒想到二分判斷條件。(我好菜啊啊啊啊啊啊啊)
對於l,r,咱們二分x的值,找到第一個hash值不想等的x。string

代碼

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e6+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;

char str[N];
string ans;
ull bin[N],hash1[N],hash2[N];
int n;
void init()
{
    bin[0]=1;
    for(int i=1; i<=n; i++)
    {
        bin[i]=bin[i-1]*137;
        hash1[i]=hash1[i-1]*137+str[i]-'a'+1;
        hash2[i]=hash2[i-1]*137+str[n-i+1]-'a'+1;
    }
}
ull get1(int l,int r)
{
    return hash1[r]-hash1[l-1]*bin[r-l+1];
}
ull get2(int l,int r)
{
    return hash2[r]-hash2[l-1]*bin[r-l+1];
}
int solve(int aga,int en)
{
    int l=0,r=(en-aga)/2,ans=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(get1(aga,aga+mid)!=get2(n+1-en,n+1-en+mid))
        {
            ans=mid;
            r=mid-1;
        }
        else
            l=mid+1;
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        getchar();
        scanf("%c",&str[i]);
    }
    init();
    int l=1,r=n;
    while(l<=r)
    {
        if(l==r)
        {
            ans+=str[l];
            break;
        }
        if(str[l]<str[r])
            ans+=str[l++];
        else if(str[l]>str[r])
            ans+=str[r--];
        else
        {
            int len=solve(l,r);
            if(str[l+len]<str[r-len])
                ans+=str[l++];
            else
                ans+=str[r--];
        }
    }
    for(int i=0; i<ans.size(); i++)
    {
        printf("%c",ans[i]);
        if((i+1)%80==0)
            printf("\n");
    }
    return 0;
}

思路2

pre[i]表示以i開頭的前綴(即把以i結尾的前綴倒過來)hash

suf[i]表示以i開頭的後綴

str[l]==str[r]的時候,咱們只須要比較pre[r]suf[l]的排名

對於pre[r],咱們在結尾加一個字符,而後把原串反過來添加到末尾,求後綴數組,pre[r]就是suf[n+n-r+2]

好比acabca,處理完就是acabca#acbaca

比較兩個c的時候,其實就是比較第一個c的排名和倒數第二個c的排名

代碼

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

char str[N];
string ans;
int n, m, sa[N], rk[N], oldrk[N<<1], pos[N], cnt[N];
bool cmp(int x, int y, int k)
{
    return oldrk[x] == oldrk[y] && oldrk[x + k] == oldrk[y + k];
}
void getsa()
{
    m = 122;
    for (int i = 1; i <= n; i++)
        ++cnt[rk[i] = str[i]];
    for (int i = 1; i <= m; i++)
        cnt[i] += cnt[i - 1];
    for (int i = n; i; i--)
        sa[cnt[rk[i]]--] = i;
    for (int k = 1; k <= n; k <<= 1)
    {
        int num = 0;
        for (int i = n - k + 1; i <= n; i++)
            pos[++num] = i;
        for (int i = 1; i <= n; i++)
        {
            if (sa[i] > k)
                pos[++num] = sa[i] - k;
        }
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
            ++cnt[rk[i]];
        for(int i=1;i<=m;i++)
            cnt[i]+=cnt[i-1];
        for (int i = n; i; i--)
            sa[cnt[rk[pos[i]]]--] = pos[i];
        num = 0;
        memcpy(oldrk, rk, sizeof(rk));
        for (int i = 1; i <= n; i++)
            rk[sa[i]]=cmp(sa[i],sa[i-1],k)?num:++num;
        if(num==n) break;
        m=num;
    }
    for(int i=1;i<=n;i++)
        rk[sa[i]]=i;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        getchar();
        scanf("%c",&str[i]);
    }
    str[n+1]='0';
    for(int i=n+2;i<=n*2+1;i++)
        str[i]=str[2*n-i+2];
    n=n*2+1;
    getsa();
    int l=1,r=n/2;
    while(l<=r)
    {
        if(str[l]<str[r]) ans+=str[l++];
        else if(str[l]>str[r]) ans+=str[r--];
        else
        {
            if(rk[l]<rk[n/2+n/2-r+2]) ans+=str[l++];
            else ans+=str[r--];
        }
    }
    for(int i=0;i<ans.size();i++)
    {
        printf("%c",ans[i]);
        if((i+1)%80==0) printf("\n");
    }
    return 0;
}
/*
6
a
c
a
b
c
b
*/

博客

相關文章
相關標籤/搜索