题目: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
个后缀排序后,排名为
定义字符串
s[0..N−1]
以第
i(0≤i<N)
个字符为中心的最长回文串长度为
pa[2i]
,以第
i
个字符和第
你需要根据
sa[0..N−1]
和
pa[0..2N−1]
来重建一个长度为
N
的字符串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
中的字符有不等关系,比如
按照
i
升序枚举
细节较多,数据还是挺强的,笔者调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[n] = -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[n] = '\0';
if(!flag)
printf("Case #%d: Wrong calculation!\n", Case);
else
printf("Case #%d: %s\n", Case, str);
}
return 0;
}