zoukankan      html  css  js  c++  java
  • hdu 5442 (ACM-ICPC2015长春网络赛F题)

    题意:给出一个字符串,长度是2*10^4。将它首尾相接形成环,并在环上找一个起始点顺时针或逆时针走一圈,求字典序最大的走法,如果有多个答案则找起始点最小的,若起始点也相同则选择顺时针。

    分析:后缀数组。

    首先,需要补充后缀数组的基本知识的话,看这个链接。

    http://www.cnblogs.com/rainydays/archive/2011/05/09/2040993.html

    我利用这道题重新整理了后缀数组的模板如下:

    const int MAX_LEN = 0;
    //call init_RMQ(f[], n) first.
    //then call query(a, b) to quest the RMQ of [a, b].
    int power[30];
    int st[MAX_LEN * 2][32];
    int ln[MAX_LEN * 2];
    
    //returns the index of the first minimum value in [x, y]
    void init_RMQ(int f[], int n)
    {
        int i, j;
        for (power[0] = 1, i = 1; i < 21; i++)
        {
            power[i] = 2 * power[i - 1];
        }
        for (i = 0; i < n; i++)
        {
            st[i][0] = i;
        }
        ln[0] = -1;
        for (int i = 1; i <= n; i++)
        {
            ln[i] = ln[i >> 1] + 1;
        }
        for (j = 1; j < ln[n]; j++)
        {
            for (i = 0; i < n; i++)
            {
                if (i + power[j - 1] - 1 >= n)
                {
                    break;
                }
                //for maximum, change ">" to "<"
                //for the last, change "<" or ">" to "<=" or ">="
                if (f[st[i][j - 1]] > f[st[i + power[j - 1]][j - 1]])
                {
                    st[i][j] = st[i + power[j - 1]][j - 1];
                }
                else
                {
                    st[i][j] = st[i][j - 1];
                }
            }
        }
    }
    
    int query(int f[], int x, int y)
    {
        if(x > y)
        {
            swap(x, y);
        }
        int k = ln[y - x + 1];
        //for maximum, change ">" to "<"
        //for the last, change "<" or ">" to "<=" or ">="
        if (f[st[x][k]] > f[st[y - power[k] + 1][k]])
            return st[y - power[k] + 1][k];
        return st[x][k];
    }
    
    //first use the constructed function
    //call function lcp(l, r) to get the longest common prefix of sa[l] and sa[r]
    //have access to the member of sa, myrank, height and so on
    //height is the LCP of sa[i] and sa[i + 1]
    class SuffixArray
    {
        public:
        char* s;
        int n, sa[MAX_LEN], height[MAX_LEN], myrank[MAX_LEN];
        int tmp[MAX_LEN], top[MAX_LEN];
    
        SuffixArray()
        {}
    
        //the string and the length of the string
        SuffixArray(char* st, int len)
        {
            s = st;
            n = len + 1;
            make_sa();
            make_lcp();
        }
    
        void make_sa()
        {
            // O(N * log N)
            int na = (n < 256 ? 256 : n);
            memset(top, 0, na * sizeof(int));
            for (int i = 0; i < n ; i++)
                top[myrank[i] = s[i] & 0xff]++;
            for (int i = 1; i < na; i++)
                top[i] += top[i - 1];
            for (int i = 0; i < n ; i++)
                sa[--top[ myrank[i]]] = i;
            int x;
            for (int len = 1; len < n; len <<= 1)
            {
                for (int i = 0; i < n; i++)
                {
                    x = sa[i] - len;
                    if (x < 0)
                        x += n;
                    tmp[top[myrank[x]]++] = x;
                }
                sa[tmp[top[0] = 0]] = x = 0;
                for (int i = 1; i < n; i++)
                {
                    if (myrank[tmp[i]] != myrank[tmp[i-1]] ||
                            myrank[tmp[i]+len]!=myrank[tmp[i-1]+len])
                        top[++x] = i;
                    sa[tmp[i]] = x;
                }
                memcpy(myrank, sa , n * sizeof(int));
                memcpy(sa , tmp, n * sizeof(int));
                if (x >= n - 1)
                    break;
            }
        }
    
        void make_lcp()
        {
            // O(4 * N)
            int i, j, k;
            for (j = myrank[height[i = k = 0] = 0]; i < n - 1; i++, k++)
            {
                while (k >= 0 && s[i] != s[sa[j - 1] + k])
                {
                    height[j - 1] = (k--);
                    j = myrank[sa[j] + 1];
                }
            }
            init_RMQ(height, n - 1);
        }
    
        int lcp(int l, int r)
        {
            return height[query(height, l, r - 1)];
        }
    };
    View Code

    先将两个原字符串拼成一个长度是2倍的字符串,便于处理越过原串末尾的情况。

    运行后缀数组求顺时针解。

    反转这个长字符串,再运行后缀数组,求逆时针解。

    比较两个解,得出最终解。

    下面我们来分析一下,每次运行后缀数组之后是如何求解的。

    设原串长度为len。

    首先从sa[len*2]开始依次向前找。即从排序最大的开始向前找。之所以排序最大的不一定是我们要的答案是因为:

    1.它可能起始点在后面的附加的串上,并没有构成完整的一圈。

    2.我们还要找起始点最小的答案呢。(当然,对于反转后的字符串,我们要找起始点最大的)

    在第一次遇到一个起始点小于len的时候,表明这个可能就是答案。但是前面可能有起始点更小的,我们还要继续沿着sa向前找,但找的时候一定要保证字典序是最大的,所以每次向前移动一个,都要观察height(i, i+1)是否>len,如果不是,则说明字典序已经不是最大了。

    特别注意,height==len的情况也是不可以的,因为两者相等很有可能表明其中一个的起始点已经等于len。当然也可以对每个都判断一下起始点位置。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define d(x) 
    
    const int MAX_N = (int)(4e4) + 100;
    
    
    //call init_RMQ(f[], n) first.
    //then call query(a, b) to quest the RMQ of [a, b].
    int power[30];
    int st[MAX_N * 2][32];
    int ln[MAX_N * 2];
    
    //returns the index of the first minimum value in [x, y]
    void init_RMQ(int f[], int n)
    {
        int i, j;
        for (power[0] = 1, i = 1; i < 21; i++)
        {
            power[i] = 2 * power[i - 1];
        }
        for (i = 0; i < n; i++)
        {
            st[i][0] = i;
        }
        ln[0] = -1;
        for (int i = 1; i <= n; i++)
        {
            ln[i] = ln[i >> 1] + 1;
        }
        for (j = 1; j < ln[n]; j++)
        {
            for (i = 0; i < n; i++)
            {
                if (i + power[j - 1] - 1 >= n)
                {
                    break;
                }
                //for maximum, change ">" to "<"
                //for the last, change "<" or ">" to "<=" or ">="
                if (f[st[i][j - 1]] > f[st[i + power[j - 1]][j - 1]])
                {
                    st[i][j] = st[i + power[j - 1]][j - 1];
                }
                else
                {
                    st[i][j] = st[i][j - 1];
                }
            }
        }
    }
    
    int query(int f[], int x, int y)
    {
        if(x > y)
        {
            swap(x, y);
        }
        int k = ln[y - x + 1];
        //for maximum, change ">" to "<"
        //for the last, change "<" or ">" to "<=" or ">="
        if (f[st[x][k]] > f[st[y - power[k] + 1][k]])
            return st[y - power[k] + 1][k];
        return st[x][k];
    }
    
    //first use the constructed function
    //call function lcp(l, r) to get the longest common prefix of sa[l] and sa[r]
    //have access to the member of sa, myrank, height and so on
    //height is the LCP of sa[i] and sa[i + 1]
    class SuffixArray
    {
        public:
        char* s;
        int n, sa[MAX_N], height[MAX_N], myrank[MAX_N];
        int tmp[MAX_N], top[MAX_N];
    
        SuffixArray()
        {}
    
        //the string and the length of the string
        SuffixArray(char* st, int len)
        {
            s = st;
            n = len + 1;
            make_sa();
            make_lcp();
        }
    
        void make_sa()
        {
            // O(N * log N)
            int na = (n < 256 ? 256 : n);
            memset(top, 0, na * sizeof(int));
            for (int i = 0; i < n ; i++)
                top[myrank[i] = s[i] & 0xff]++;
            for (int i = 1; i < na; i++)
                top[i] += top[i - 1];
            for (int i = 0; i < n ; i++)
                sa[--top[ myrank[i]]] = i;
            int x;
            for (int len = 1; len < n; len <<= 1)
            {
                for (int i = 0; i < n; i++)
                {
                    x = sa[i] - len;
                    if (x < 0)
                        x += n;
                    tmp[top[myrank[x]]++] = x;
                }
                sa[tmp[top[0] = 0]] = x = 0;
                for (int i = 1; i < n; i++)
                {
                    if (myrank[tmp[i]] != myrank[tmp[i-1]] ||
                            myrank[tmp[i]+len]!=myrank[tmp[i-1]+len])
                        top[++x] = i;
                    sa[tmp[i]] = x;
                }
                memcpy(myrank, sa , n * sizeof(int));
                memcpy(sa , tmp, n * sizeof(int));
                if (x >= n - 1)
                    break;
            }
        }
    
        void make_lcp()
        {
            // O(4 * N)
            int i, j, k;
            for (j = myrank[height[i = k = 0] = 0]; i < n - 1; i++, k++)
            {
                while (k >= 0 && s[i] != s[sa[j - 1] + k])
                {
                    height[j - 1] = (k--);
                    j = myrank[sa[j] + 1];
                }
            }
            init_RMQ(height, n - 1);
        }
    
        int lcp(int l, int r)
        {
            return height[query(height, l, r - 1)];
        }
    };
    
    SuffixArray suffix;
    int len;
    char ans1[MAX_N], ans2[MAX_N];
    int pos1, pos2;
    char s[MAX_N];
    
    int work1(char* ans)
    {
        int p = len * 2;
        while (suffix.sa[p] >= len)
            p--;
        for (int i = p - 1; i >= 0; i--)
        {
            if (suffix.lcp(i, i + 1) <= len)
                break;
            if (suffix.sa[i] < suffix.sa[p])
            {
                p = i;
            }
        }
        memcpy(ans, s + suffix.sa[p], len);
        ans[len] = 0;
        return suffix.sa[p];
    }
    
    int work2(char* ans)
    {
        int p = len * 2;
        while (suffix.sa[p] >= len)
            p--;
        for (int i = p - 1; i >= 0; i--)
        {
            if (suffix.lcp(i, i + 1) <= len)
                break;
            if (suffix.sa[i] > suffix.sa[p])
            {
                p = i;
            }
        }
        memcpy(ans, s + suffix.sa[p], len);
        ans[len] = 0;
        return suffix.sa[p];
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while (t--)
        {
            scanf("%d", &len);
            scanf("%s", s);
            for (int i = 0; i < len; i++)
            {
                s[len + i] = s[i];
            }
            suffix = SuffixArray(s, len * 2);
            pos1 = work1(ans1);
            reverse(s, s + len * 2);
            suffix = SuffixArray(s, len * 2);
            pos2 = work2(ans2);
            pos2 = len - 1 - pos2;
    
            if (strcmp(ans1, ans2) > 0)
            {
                printf("%d 0
    ", pos1 + 1);
                continue;
            }else if (strcmp(ans1, ans2) < 0)
            {
                printf("%d 1
    ", pos2 + 1);
                continue;
            }else if (pos1 <= pos2)
            {
                printf("%d 0
    ", pos1 + 1);
                continue;
            }else
            {
                printf("%d 1
    ", pos2 + 1);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File
    android的五大布局(layout)
    json数据进行格式化
    将utf-8的中文或者字符都看成一个字符
    Mysql 中 trim 的用法
    生成密码函数
    Eclipse智能提示设置
    Java Jersey2使用总结
    Java对存储过程的调用方法
    Jersey框架
  • 原文地址:https://www.cnblogs.com/rainydays/p/4806883.html
Copyright © 2011-2022 走看看