【HackerRank】Functional Palindromes(回文树+后缀数组+lcp排序+字符串哈希+二分)

【HackerRank】Functional Palindromes(回文树+后缀数组+lcp排序+字符串哈希+二分)

这个页面抓不太好,大家点进去看吧~~

做过的用到数据结构+算法最多的一个题……真真是做ACM以来做的最最麻烦的一个题……

说白了其实就是板子大杂烩……但是会吐的那种。。

此外……此题价值75$……不要问我为什么。。。TOT

现在进入正片——

给你一个长n的字符串,仅由小写字母组成。然后q次询问。
每次询问该串的所有回文子串里,按字典序从小到大后第k个的回文串。输出它的f函数值。注意回文串不去重。

具体要做的就是,预处理母串的所有回文串,按字典序排序,然后快速找到第k个,输出对应f值。

分为如下子问题:
1·预处理母串所有回文子串——回文树,具体百度,然后与回文树恶战一场吧……!¥@#¥。然后可以把本质不同的回文子串以及对应的出现次数存起来,然后排序。存回文子串可以存该种回文子串出现的左右下标——用以排序。

2·把回文子串按字典序排序——预处理母串的后缀数组,然后可以搞RMQ,弄出来个快速lcp。这样对于上一步存起来的本质不同的回文子串左右下标,通过比较lcp,可以达到快速按字典序排序。

3·快速找到第k个——二分,预处理到某个串所需要的k的下界,这样可以快速二分出第k个串。

4·f函数——字符串哈希,一套板子生砸上……

然后就出来了……GG。。。

因为是边学边造轮子,所以代码比较丑陋……
代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
#define LL long long
#define Pr pair<int,int>

using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;

const int MAXN = 112345;
int t1[MAXN],t2[MAXN],c[MAXN];

bool cp(int *r,int a,int b,int l)
{
    return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(char *str,int *sa,int *rk,int *height,int n,int m)
{
    n++;
    int i,j,p,*x = t1,*y = t2;
    for(i = 0; i < m; ++i) c[i] = 0;
    for(i = 0; i < n; ++i) c[x[i] = str[i]]++;
    for(i = 1; i < m; ++i) c[i] += c[i-1];
    for(i = n-1; i >= 0; --i) sa[--c[x[i]]] = i;
    for(j = 1; j <= n; j <<= 1)
    {
        p = 0;

        for(i = n-j; i < n; ++i) y[p++] = i;
        for(i = 0; i < n; ++i) if(sa[i] >= j) y[p++] = sa[i]-j;

        for(i = 0; i < m; ++i) c[i] = 0;
        for(i = 0; i < n; ++i) c[x[y[i]]]++;
        for(i = 1; i < m; ++i) c[i] += c[i-1];
        for(i = n-1; i >= 0; --i) sa[--c[x[y[i]]]] = y[i];

        swap(x,y);
        p = 1;
        x[sa[0]] = 0;

        for(i = 1; i < n; ++i)
            x[sa[i]] = cp(y,sa[i-1],sa[i],j)? p-1: p++;
        if(p >= n) break;
        m = p;
    }
    int k = 0;
    n--;
    for(i = 0; i <= n; ++i) rk[sa[i]] = i;
    for(i = 0; i < n; ++i)
    {
        if(k) k--;
        j = sa[rk[i]-1];
        while(str[i+k] == str[j+k]) k++;
        height[rk[i]] = k;
    }
}

int rk[MAXN],height[MAXN];
int RMQ[MAXN];
int mm[MAXN];
int best[20][MAXN];

void initRMQ(int n)
{
    mm[0] = -1;
    for(int i = 1; i <= n; ++i)
        mm[i] = ((i&(i-1)) == 0)? mm[i-1]+1: mm[i-1];
    for(int i = 1; i <= n; ++i) best[0][i] = i;
    for(int i = 1; i <= mm[n]; i++)
        for(int j = 1; j +(1<<i)-1 <= n; j++)
        {
            int a = best[i-1][j];
            int b = best[i-1][j+(1<<(i-1))];
            if(RMQ[a] < RMQ[b]) best[i][j] = a;
            else best[i][j] = b;
        }
}

int askRMQ(int a,int b)
{
    int t;
    t = mm[b-a+1];
    b -= (1<<t)-1;
    a = best[t][a];
    b = best[t][b];
    return RMQ[a] < RMQ[b]? a: b;
}

int lcp(int a,int b,int len)
{
    if(a == b) return len-a;
    a = rk[a];
    b = rk[b];
    if(a > b) swap(a,b);
    return height[askRMQ(a+1,b)];
}

struct node {
    int next[26];
    int len;
    int sufflink;
    int num,l,r;
};

char s[112345];
node tree[MAXN];
int num;            // node 1 - root with len -1, node 2 - root with len 0
int suff;           // max suffix palindrome

bool addLetter(int pos) {
    int cur = suff, curlen = 0;
    int let = s[pos] - 'a';

    while (true) {
        curlen = tree[cur].len;
        if (pos - 1 - curlen >= 0 && s[pos - 1 - curlen] == s[pos])
            break;
        cur = tree[cur].sufflink;
    }
    if (tree[cur].next[let]) {
        suff = tree[cur].next[let];
        tree[suff].num++;
        return false;
    }

    num++;
    suff = num;
    tree[num].len = tree[cur].len + 2;
    tree[num].r = pos;
    tree[num].l = pos-tree[num].len+1;
    tree[cur].next[let] = num;

    if (tree[num].len == 1) {
        tree[num].sufflink = 2;
        tree[num].num = 1;
        return true;
    }

    tree[num].num++;
    while (true) {
        cur = tree[cur].sufflink;
        curlen = tree[cur].len;
        if (pos - 1 - curlen >= 0 && s[pos - 1 - curlen] == s[pos]) {
            tree[num].sufflink = tree[cur].next[let];
            break;
        }
    }
    return true;
}

void initTree() {
    num = 2; suff = 2;
    tree[1].len = -1; tree[1].sufflink = 1;
    tree[2].len = 0; tree[2].sufflink = 1;
}

int len;
int r[MAXN];
int sa[MAXN];
vector <int> adj[MAXN];
vector <pair<Pr,int> > vc;

void dfs(int u)
{
    for(int i = 0; i < adj[u].size(); ++i)
    {
        dfs(adj[u][i]);
        tree[u].num += tree[adj[u][i]].num;
    }
}

bool cmp(pair<Pr,int> a,pair<Pr,int> b)
{
    int l1,l2,r1,r2,len1,len2;
    l1 = a.first.first;
    l2 = b.first.first;
    r1 = a.first.second;
    r2 = b.first.second;
    len1 = r1-l1+1;
    len2 = r2-l2+1;

    int cp = lcp(l1,l2,len);

    if(cp >= len1 || cp >= len2) return len1 <= len2;
    else return s[l1+cp] <= s[l2+cp];
}

int P[MAXN],HashF[MAXN],HashR[MAXN];
class RollingHash {
    public:
        RollingHash() {
            prime = 100001;
            mod1 = 1000000007;
            mod2 = 1897266401;
            P[0] = 1;
            for(int i=1; i<MAXN; i++) {
                P[i] = 1LL * P[i-1] * prime % mod1;
            }
        }

        void Construct() {
            HashF[0] = HashR[ len+1 ] = 0;
            for(int i=1; i<=len; i++) {
                HashF[i] = ( 1LL * HashF[i-1] * prime + s[i-1] ) % mod1;
                HashR[len-i+1] = ( 1LL * HashR[len-i+2] * prime + s[ len - i ] ) % mod1;
            }
        }

        int GetForwardHash( int l, int r ) {
            if( l == 1 ) return HashF[r];
            int hash = HashF[r] - 1LL * HashF[l-1] * P[ r - l + 1 ] % mod1;
            if( hash < 0 ) hash += mod1;
            return hash;
        }
        int GetBackwardHash( int l, int r ) {
            if( r == len ) return HashR[l];
            int hash = HashR[l] - 1LL * HashR[r+1] * P[ r - l + 1 ] % mod1;
            if( hash < 0 ) hash += mod1;
            return hash;
        }
        bool IsPalin( int l, int r ) {
            if( r < l ) return true;
            return (GetForwardHash(l, r) == GetBackwardHash(l, r));
        }

    private:
        int prime, mod1, mod2;
};

vector <pair<LL,int> > by;
LL ans[MAXN];
LL cnt;

void init()
{
    da(s,sa,rk,height,len,128);
    for(int i = 1; i <= len; ++i) RMQ[i] = height[i];
    initRMQ(len);
    initTree();
    for (int i = 0; i < len; i++)
        addLetter(i);

    for(int i = 2; i <= num; ++i)
        adj[tree[i].sufflink].push_back(i);

    dfs(1);

    for(int i = 3; i <= num; ++i)
        vc.push_back(pair<Pr,int> (Pr(tree[i].l,tree[i].r),tree[i].num));

    sort(vc.begin(),vc.end(),cmp);

    RollingHash obj;
    obj.Construct();

    cnt = 1;
    for(int i = 0; i < vc.size(); ++i)
    {
        by.push_back(pair<LL,int> (cnt,i));
        ans[i] = obj.GetForwardHash(vc[i].first.first+1,vc[i].first.second+1);
        //printf("%d %d %lld\n",vc[i].first.first+1,vc[i].first.second+1,ans[i]);
        cnt += vc[i].second;
    }

}



int main()
{
    int q;
    LL k;

    scanf("%d%d",&len,&q);
    scanf("%s",s);

    init();

    int pos;

    while(q--)
    {
        scanf("%lld",&k);
        if(k >= cnt) puts("-1");
        else
        {
            pos = -1;

            int l = 0,r = by.size()-1;

            while(l <= r)
            {
                int mid = (l+r)>>1;
                if(k >= by[mid].first)
                {
                    pos = mid;
                    l = mid+1;
                }
                else r = mid-1;
            }

            printf("%lld\n",ans[by[pos].second]);
        }
    }


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值