您的位置:首页 > 其它

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,字符串只能包含小写字母,如果无解则输出
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息