首先是比较水的Manacher
用于求最长回文子串
名字很牛叉的样纸,实际上相当水。
首先在每两个字符间插入‘#’字符(和网上的方法不一样,因为两端实际上要插入不用的字符来使while停止)
于是我们可以只需考虑求长度为奇数的回文串
记P[i]为向右最长拓展到的元素(此处包括了‘#’字符)(也就是以i为中心的最长回文串长度, 不包括‘#’字符)
考虑如何通过p[1] .. p[i - 1] 推出 p[i]
考虑在求p[i]的同时维护p中最大元素pmax和其位置pos
下面上代码。因为实在不想讲,也讲不清..不如代码简洁暴力。
#include<cstdio>
#define min(a, b) ({int _ = a, __ = b; _ < __ ? _ : __;})
#define ot "%d"
#define maxn 1005
using namespace std;
int p[maxn], n;
char s[maxn], c;
int manacher()
{
int pos, pmax = 0;
for (int i = 1; i < n; ++i)
{
if (pmax > i) p[i] = min(pmax - i, p[(pos << 1) - i]); else p[i] = 1;
while (s[i - p[i]] == s[i + p[i]]) ++p[i];
if (p[i] + i > pmax) pmax = p[i] + i, pos = i;
}
return pmax;
}
int main()
{
n = 0;
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
while ((c = getchar()) != '\n') s[++n] = c, s[++n] = '#';
s[0] = '@'; s[n] = '$'; --n; //此处假设字符集为大小写字母或数字
printf(ot, manacher());
return 0;
}
肯定有人想问为什么是O(n)的。
事(系)实(际)上,注意到while() 的执行总次数是O(n)的。(呵呵...真NC)不证了,1是因为网上到处有,2是想想就明白了...
那么就是这样了。
接下来是SA。
【郑重提示:千万别看论文的代码!可读性实在是相当那啥....TAT(或者说我太弱了?),看了半天才看懂】
Suffix Array 这个东西嘛..顾名思义和后缀有关...网上各种介绍很多。基础的就不说了。
主要说说倍增是怎么回事。网上说的太不清楚了。
首先我们要知道桶排的原理。
比如:
给定{An},将其排序后输出。(n ≤ 10 ^ 7)(保证0 ≤ Ai ≤ 10 ^ 7)
怎么做呢?qsort超时无悬念啊。
我们开一个b数组,b[i] 表示 值为 i 的数字 在{An} 中出现了多少次。
显然O(n)求出。
然后我们就可以利用B输出了。(好2B...)这就是桶排。
基数排序则可以看做几次桶排。知道桶排基数排序应该就很好理解了。
那么我们如何利用基数排序呢?
神奇的 倍增出现了!
还是那张老图。
怎么理解呢?
我们记 suffix(s, i) 为s的第i长的后缀(即从第i个字符开始的后缀)
记 prefix(s, i) 为s的第i长的前缀(即到第i个字符结尾的前缀)
对于那么 对于给定的k(k ≥ 1),将所有形如prefix(suffix(s, i), 2^k)的字符串(后缀的前缀好纠结..)排序
我们可以得出一个临时键值(rank)。如何得出呢?
首先 rank(prefix(suffix(s, i), 1)) 可以直接得出。(直接是AscII码就行了)
然后我们假设已经知道 prefix(suffix(x, i), 2 ^ (k - 1)) , prefix(suffix(x, i + 2 ^ (k - 1)), 2 ^ (k - 1)) 两个数
以这两个数作为信息,将其基数排序即可得出新的rank。
(好像说的还是不是很清楚...ORZ.....)
那么就可以求出来了嘛...
但是有什么用呢。
我们 又引入了两个个数组:height, h(真恶心啊..这么多数组),
令height[i]=LCP(suffix(sa[i-1]), suffix(sa[i])), h[i]=height[Rank[i]], (height[i]=h[SA[i]])
也就是h[i] 表示 排名为i的后缀和在其前一名的后缀的最长公共前缀。
然后有一个神奇的性质:h[i] >= h[i-1]-1
这是为什么捏?
我们不妨设suffix(k)是排在suffix(i-1)前一名的后缀,则它们的最长公共前缀是h[i-1]。
那么suffix(k+1)将排在suffix(i)的前面(这里要求h[i-1]>1,如果h[i-1]≤1,原式显然成立)
并且suffix(k+1)和suffix(i)的最长公共前缀是h[i-1]-1。
所以suffix(i)和在它前一名的后缀的最长公共前缀至少是h[i-1]-1。
按照h[1],h[2],……,h[n]的顺序计算,并利用h 数组的性质,时间复杂度可以降为O(n)。
那么就是这样了。
关于代码:如果想理解的话..请看这个。
http://hi.baidu.com/pianoeater/item/7b08ef60bf77063869105b9d
至于我写的. 这是个裸的...不过看还是看得过去吧。
有些地方应该更好理解。
#include<cstdio>
#include<cstring>
#include<climits>
#define maxlen 1005
#define FOR(i, a, b) for (int i = a; i <= b; ++i)
#define ROF(i, b, a) for (int i = b; i >= a; --i)
#define ms(a, b) memset((a), (b), sizeof (a))
int n, len, bk[maxlen], x[maxlen], y[maxlen], sa[maxlen], rank[maxlen], a[maxlen], h[maxlen];
char s[maxlen];
using namespace std;
void work_sa()
{
ms(bk, 0); //bk : bucket
FOR (i, 1, len) bk[rank[i] = a[i]]++;
FOR (i, 1, n) bk[i] += bk[i-1];
ROF (i, len, 1) sa[bk[rank[i]]--] = i; //Initialization
for (int q = 1, p, m; p < len; q <<= 1, m = p)
{
p = 0; // p means the max rank, also does m
FOR (i, len - q + 1, len) y[++p] = i; // Well, actually I don't understand it .... TAT
FOR (i, 1, len) if (sa[i] > q) y[++p] = sa[i] - q;
FOR (i, 1, len) x[i] = rank[y[i]];
ms(bk, 0);
FOR (i, 1, len) bk[x[i]]++;
FOR (i, 1, n) bk[i] += bk[i-1];
ROF (i, len, 1) sa[bk[x[i]]--] = y[i];
memcpy(y, rank, sizeof y);
rank[sa[1]] = 1, p = 1;
FOR (i, 2, len)
rank[sa[i]] = ((y[sa[i]] == y[sa[i - 1]]) && (y[sa[i] + q] == y[sa[i - 1] + q])) ? p : ++p;
}
}
void work_h()
{
int k = 0, j;
for (int i = 1; i <= len; h[rank[i++]] = k)
for (k ? --k : 0, j = sa[rank[i] - 1]; a[i + k] == a[j + k]; ++k);
}
int main()
{
gets(s + 1); len = strlen(s + 1); int l = INT_MAX; n = 0;
FOR (i, 1, len) a[i] = (int)s[i], l <?= a[i], n >?= a[i];
n -= --l; FOR (i, 1, len) a[i] -= l;
work_sa();
work_h();
return 0;
}