BZOJ 1692: [Usaco2007 Dec]队列变换

嗯~有是后缀数组呢


显然不考虑后缀树组,这个题目的做法显然是类似贪心的方法来搜索

从左边,右边选一个ASCII码最小的一个先丢过去。但是如果AABCAA这样的串就会出现不知道先选哪一个的情况了


假设f[i]表示从第i个开始整个串的后缀 (上面的例子:f[1] = AABCAA   f[3]=BCAA)  

g[i]表示从i开始,他的前缀(上面的例子 g[4] = CBAA  g[3]=BAA)


那么我们在贪心的时候,碰到相同的情况下,只需要比较f[L]和g[R]的大小就可以选哪一个了。

为什么呢……

显然如果f[L]>g[R]的话,那么假如 s[L + 0..p ] == s[R - 0..p] 相等,下一s[L + p + 1] > s[R - p -1]一定会不相等,并且s[R-p-1]更优,如果完全相等那么说明怎么取都一样。


后缀数组可以解决他

直接给串的后面加一个'0' 然后再加一个反过来的穿在后面

比如ABCD就变为

ABCD0DCBA


这样求出后缀数组的rank就有一些作用


因为ABCD

f[1]对应的串就是ABCD0DCBA  也就是rank[1]

g[4]对应的就是DCBA  也就是rank[6]


然后可以通过对比rank来得到正确解


ACcode:   不优化380+ms  稍微优化了一点读入输出就跑到240+ms但是后缀数组写的不犀利没法到第一……


#include <cstdio>

const int max_n = 30000 * 2 + 10;
const int max_size = max_n * 1;
 
int n;
int sa[max_size], wa[max_size], wb[max_size];
int tong[max_size],wv[max_size];
char str[max_size];
int rank[max_size], h[max_size];
 

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


void da(char *r, int *sa, int n, int m)
{
     int i,j,p, *x = wa, *y = wb, *t;
     for (i = 0; i < m; ++ i)    tong[i] = 0;
     for (i = 0; i < n; ++ i)    tong[x[i] = r[i]] ++;
     for (i = 1; i < m; ++ i)    tong[i] += tong[i - 1];
     for (i = n - 1; i >= 0; -- i)       sa[-- tong[x[i]]] = i;
     for (j = 1, p =1; p < n; j *= 2, m = p)
     {
         for (p = 0, 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) tong[i] = 0;
         for (i = 0; i < n; ++ i) wv[i] = x[y[i]];
         for (i = 0; i < n; ++ i) tong[wv[i]] ++;
         for (i = 1; i < m; ++ i) tong[i] += tong[i - 1];
         for (i = n - 1; i >= 0; -- i)   sa[--tong[wv[i]]] = y [i];
         for (t =x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; ++ i)
             x[sa[i]] = cmp(y, sa[i - 1], sa[i], j)?p-1:p++;
     }
}
 
inline void geth(char *r, int *sa, int n)
{
     int i, j, k = 0;
     for (i = 1; i <= n; ++ i)      rank[sa[i]] = i;
     for (i = 0; i < n; h[rank[i ++]] = k)
         for (k?k--:0, j = sa[rank[i] - 1]; r[i + k]==r[j + k]; ++ k);
}

char ch;
inline void read(char &x)
{
       while (ch=getchar(),ch>'Z' || ch<'A') ;
       x=ch;
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++ i)      read(str[i]);
    for (int i = 0; i < n; ++ i)      str[i] -= ('A' - 1);
    str[n] = 0;
    int tot = n * 2 + 1;
    str[tot] = 0;
    for (int i = 1; i <= n; ++ i)  str[n + i] = str[n - i];
    da(str, sa, tot + 1, 28);    
    geth(str, sa, tot);
    int A = 0, B = n + 1;
    while (A + B - n - 1 < n)
    {
          if (rank[A] < rank[B])       putchar(str[A++] + 'A' - 1);
          else putchar( str[B++] + 'A' - 1);        
          if (!((A + B - n - 1)%80))  putchar('\n');
    }
    return 0;
}






发布了12 篇原创文章 · 获赞 1 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览