Regional 2015 - Asia EC Final - C Suffixes and Palindromes
2016-06-22 08:07
381 查看
题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=719&page=show_problem&problem=5524
题意:
定义字符串s[0..N−1]以第i(0≤i<N)个字符到末尾组成的子串称为后缀i(suf[i]=s[i..N−1]),将所有的N个后缀排序后,排名为i(0≤i<N)的是后缀sa[i]。
定义字符串s[0..N−1]以第i(0≤i<N)个字符为中心的最长回文串长度为pa[2i],以第i个字符和第(i+1)个字符之间为中心的最长回文串长度为pa[2i+1]。
你需要根据sa[0..N−1]和pa[0..2N−1]来重建一个长度为N的字符串s,字符串只能包含小写字母,如果无解则输出
2≤N≤105,sa[0..N−1]isapermutation,0≤pa[i]≤N。
题解:
首先,只考虑sa[0..N−1]的话,可以得到s[sa[i−1]]≤s[sa[i]],这样得到了一个偏序关系。
其次,考虑什么时候必须有s[sa[i−1]]<s[sa[i]],这必然是suf[sa[i−1]+1]>suf[sa[i]+1],令rk[i]表示suf[i]在后缀中的排名,则上述条件即为rk[sa[i−1]+1]>rk[sa[i]+1],根据这个大于关系可以建一条有向边。
再者,考虑pa[0..2N−1],与
模拟
在
按照i升序枚举s[sa[i]]的取值,根据之前的有向边和s[sa[i−1]]≤s[sa[i]]的关系,来计算最小的s[sa[i]],如果存在区间缩成点后有自相矛盾的大于关系或者是小写字母不够用则无解,否则很容易证明得到的解s[0..N−1]是字典序最小的,总时间复杂度O(N)。
细节较多,数据还是挺强的,笔者调bug调了一晚上……
以上都是偷懒的产物,本题只需将等于关系看成两个大于等于关系,直接建图跑差分约束即可。
代码:
题意:
定义字符串s[0..N−1]以第i(0≤i<N)个字符到末尾组成的子串称为后缀i(suf[i]=s[i..N−1]),将所有的N个后缀排序后,排名为i(0≤i<N)的是后缀sa[i]。
定义字符串s[0..N−1]以第i(0≤i<N)个字符为中心的最长回文串长度为pa[2i],以第i个字符和第(i+1)个字符之间为中心的最长回文串长度为pa[2i+1]。
你需要根据sa[0..N−1]和pa[0..2N−1]来重建一个长度为N的字符串s,字符串只能包含小写字母,如果无解则输出
Wrong calculation!,如果有解或多解则输出字典序最小解。
2≤N≤105,sa[0..N−1]isapermutation,0≤pa[i]≤N。
题解:
首先,只考虑sa[0..N−1]的话,可以得到s[sa[i−1]]≤s[sa[i]],这样得到了一个偏序关系。
其次,考虑什么时候必须有s[sa[i−1]]<s[sa[i]],这必然是suf[sa[i−1]+1]>suf[sa[i]+1],令rk[i]表示suf[i]在后缀中的排名,则上述条件即为rk[sa[i−1]+1]>rk[sa[i]+1],根据这个大于关系可以建一条有向边。
再者,考虑pa[0..2N−1],与
manacher算法得到的信息相同,可以利用
manacher算法所涉及到的O(N)个等于或不等关系来加强限制,减少冗余计算。
模拟
manacher算法过程,若遇到字符之间的相等关系,比如是s[L]=s[R],结合sa[rk[L]]和sa[rk[R]]的关系,可以知道sa[i](rk[L]≤i≤rk[R])都是相等的,这个可以利用并查集将区间缩成点。
在
manacher匹配过程结束时,遇到的必须是字符之间的不等关系,如果不是则无解,如果是两个s中的字符有不等关系,比如s[L]≠s[R],结合sa[rk[L]]和sa[rk[R]]的关系,得到一个大于关系并建一条有向边。
按照i升序枚举s[sa[i]]的取值,根据之前的有向边和s[sa[i−1]]≤s[sa[i]]的关系,来计算最小的s[sa[i]],如果存在区间缩成点后有自相矛盾的大于关系或者是小写字母不够用则无解,否则很容易证明得到的解s[0..N−1]是字典序最小的,总时间复杂度O(N)。
细节较多,数据还是挺强的,笔者调bug调了一晚上……
以上都是偷懒的产物,本题只需将等于关系看成两个大于等于关系,直接建图跑差分约束即可。
代码:
#include <stdio.h> #include <cstring> #include <algorithm> const int maxn = 100010, maxs = 26; int t, n, sa[maxn], rk[maxn], ma[maxn << 1], tot, lnk[maxn], fa[maxn]; char str[maxn]; struct Edge { int nxt, v; } e[maxn << 2]; inline void scan(int &x) { register int ch; while((ch = getchar()) < '0' || ch > '9'); for(x = ch - '0'; (ch = getchar()) >= '0' && ch <= '9'; x = (x << 3) + (x << 1) + (ch - '0')); } int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } int main() { scan(t); for(int Case = 1; Case <= t; ++Case) { scan(n); tot = 0; memset(lnk, -1, n * sizeof(int)); for(int i = 0; i < n; ++i) { scan(sa[i]); rk[sa[i]] = i; fa[i] = i; } rk = -1; for(int i = 1; i < n; ++i) if(rk[sa[i - 1] + 1] > rk[sa[i] + 1]) { e[tot] = (Edge){lnk[i], i - 1}; lnk[i] = tot++; } ma[0] = -1; ma[1] = ma[n << 1 | 1] = 0; for(int i = 2; i <= n << 1; ++i) scan(ma[i]); bool flag = 1; for(int i = 2, mx = 1, id = 1; i <= n << 1 && flag; ++i) { int cur = mx > i ? std::min(mx - i, ma[(id << 1) - i] + 1) : 0, pL, pR; flag &= cur <= ma[i] + 1 && i - ma[i] > 0 && i + ma[i] < n + 1 << 1; for(pL = i - cur, pR = i + cur; cur <= ma[i] && flag; ++cur, --pL, ++pR) if((~pL & 1) && pL < pR) { int u = rk[(pL >> 1) - 1], v = rk[(pR >> 1) - 1]; if(u > v) std::swap(u, v); for(u = find(u), v = find(v); u < v && flag; v = find(v)) { for(int it = lnk[v]; it != -1 && flag; it = e[it].nxt) flag &= e[it].v < u; fa[v] = find(v - 1); } } if((flag &= (~pL & 1)) && pL >= 2 && pR <= n << 1) { pL = rk[(pL >> 1) - 1]; pR = rk[(pR >> 1) - 1]; if(pL > pR) std::swap(pL, pR); e[tot] = (Edge){lnk[pR], pL}; lnk[pR] = tot++; } if(mx < i + cur) { mx = i + cur; id = i; } } char last = 'a'; for(int i = 0, j = 0; i < n && flag; i = j) { char cur = last; for(++j; j < n && find(i) == find(j); ++j); for(int k = i; k < j && flag; ++k) for(int it = lnk[k]; it != -1 && flag; it = e[it].nxt) if(find(e[it].v) == find(i)) flag = 0; else if(cur <= str[sa[e[it].v]]) cur = str[sa[e[it].v]] + 1; if(cur > 'z') flag = 0; else { for( ; i < j; ++i) str[sa[i]] = cur; last = cur; } } str = '\0'; if(!flag) printf("Case #%d: Wrong calculation!\n", Case); else printf("Case #%d: %s\n", Case, str); } return 0; }
相关文章推荐
- JavaScript构造函数详解
- jQuery入门 构造函数
- js调用webservice构造SOAP进行身份验证
- 使用Java构造和解析Json数据的两种方法(详解二)
- 使用Java构造和解析Json数据的两种方法(详解一)
- java中的静态代码块、构造代码块、构造方法详解
- Python类方法__init__和__del__构造、析构过程分析
- 关于获得ArrayAdapter对象的细节
- 类的继承与派生
- 探讨“临时对象”(temporary object)
- 初学图论-Kahn拓扑排序算法(Kahn's Topological Sort Algorithm)
- 初学图论-Bellman-Ford单源最短路径算法
- 初学图论-DAG单源最短路径算法
- 初学图论-Dijkstra单源最短路径算法
- 初学图论-Dijkstra单源最短路径算法基于优先级队列(Priority Queue)的实现
- 封装好的Folyd建图,C++源码
- LCA模板
- 图论学习笔记之一——Floyd算法
- soj1041. Pushing Boxes
- Life Forms 后缀数组 不小于k个字符串中的最长子串