poj 3623 Best Cow Line, Gold 後綴數組 + 貪心
阿新 • • 發佈:2018-01-28
依次 clu sca rank getch suffix ios fine else
題目鏈接
題目描述
對於一個給定的字符串,可以從左右兩端取字符,依次排列構成一個新的字符串。
求可能構成的字符串中字典序 最小的一個。
例:ACDBCB -> ABCBCD
思路
參考自 xueyifan1993.
正確的 貪心 姿勢:
記左端位置為 \(l\),右端位置為 \(r\),比較 \(suffix(l)\) 與 \(inv(prefix(r))\),取小者。
比較 則顯然是借助 \(rank\) 數組。
// 以及正確的I/O姿勢真的重要...
// scanf+printf:1172ms -> getchar+putchar: 219ms
Code
#include <stdio.h> #include <iostream> #define maxn 60010 using namespace std; typedef long long LL; int a[maxn], wa[maxn], wb[maxn], wv[maxn], wt[maxn], h[maxn], rk[maxn], sa[maxn], n, r[maxn]; char s[maxn], ans[maxn]; bool cmp(int* r, int a, int b, int l) { return r[a] == r[b] && r[a+l] == r[b+l]; } void init(int* r, int* sa, int n, int m) { int* x=wa, *y=wb, *t, i, j, p; for (i = 0; i < m; ++i) wt[i] = 0; for (i = 0; i < n; ++i) ++wt[x[i] = r[i]]; for (i = 1; i < m; ++i) wt[i] += wt[i - 1]; for (i = n-1; i >= 0; --i) sa[--wt[x[i]]] = i; for (j = 1, p = 1; p < n; j <<= 1, 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 < n; ++i) wv[i] = x[y[i]]; for (i = 0; i < m; ++i) wt[i] = 0; for (i = 0; i < n; ++i) ++wt[wv[i]]; for (i = 1; i < m; ++i) wt[i] += wt[i - 1]; for (i = n-1; i >= 0; --i) sa[--wt[wv[i]]] = y[i]; t = x, x = y, y = t, x[sa[0]] = 0; for (p = 1, i = 1; i < n; ++i) x[sa[i]] = cmp(y, sa[i], sa[i-1], j) ? p - 1 : p++; } for (i = 0; i < n; ++i) rk[sa[i]] = i; } inline void read(char &x) { char ch; while (ch=getchar(),ch>'Z' || ch<'A') ; x=ch; } int main() { scanf("%d", &n); int m = 0; for (int i = 0; i < n; ++i) { read(s[i]); r[2*n-1-i] = r[i] = s[i]; m = max(r[i], m); } int tot = n << 1; r[tot++] = 0; init(r, sa, tot, ++m); int l = 0, r = n-1, cnt=0; while (l < r) { if (rk[l] > rk[2*n-1-r]) ans[cnt++] = s[r--]; else ans[cnt++] = s[l++]; } ans[cnt++] = s[l]; for (int i = 0; i < cnt; ++i) { if (i && i % 80==0) puts(""); putchar(ans[i]); } return 0; }
poj 3623 Best Cow Line, Gold 後綴數組 + 貪心