[APIO2014]回文串

嘟嘟嘟


这道题真的挺好的,虽然数据很水,还卡空间。
有多水呢?建完SAM后,把他和反串匹配一遍,就能得90分……这个做法显然是不对的,比如abcweabc,求出来是3,但答案应该是2.


因为我不会回文自动机,所以就学了一下题解的SAM+manacher的做法。
建完SAM后,开始用manacher求回文子串,每求出一个本质不同的回文子串,就把这个子串放到SAM上跑,更新答案。本质不同的回文子串就是令p[i]增长的串(但我现在不是很清楚为啥)。
但这样的复杂度是\(O(n ^ 2)\)的,得优化一下。
找回文串\(O(n)\)是肯定没办法再优化的,只能优化在SAM上跑的过程。
因为每一个子串所属的节点在SAM上是一定存在的,所以我们可以用倍增的方法优化找该节点的过程。
建SAM的时候,记录每一个前缀在parent tree上深度最深的节点,这样这个前缀的所有后缀所在的节点要么是它本身,要么是他的祖先节点。因此预处理倍增数组就OK了。
所以复杂度是\(O(nlogn)\)


因为我把manacher给忘了,所以代码中manacher的部分借鉴了一下hzwer大仙的……


差点忘了,这题就给128MB,所以空间得卡卡,比如转移数组开到tra[maxn][27],而不是tra[maxn][30]……

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
-typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 6e5 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

ll ans = 0;
char s[maxn >> 1];
struct Sam
{
  int las, cnt;
  int tra[maxn][27], len[maxn], link[maxn], siz[maxn], a[maxn >> 1];
  In void init() {link[las = cnt = 0] = -1;}
  In void insert(int c, int id)
  {
    int now = ++cnt, p = las; a[id] = now;
    len[now] = len[las] + 1; siz[now] = 1;
    while(~p && !tra[p][c]) tra[p][c] = now, p = link[p];
    if(p == -1) link[now] = 0;
    else
      {
	int q = tra[p][c];
	if(len[q] == len[p] + 1) link[now] = q;
	else
	  {
	    int clo = ++cnt;
	    memcpy(tra[clo], tra[q], sizeof(tra[q]));
	    len[clo] = len[p] + 1;
	    link[clo] = link[q], link[q] = link[now] = clo;
	    while(~p && tra[p][c] == q) tra[p][c] = clo, p = link[p];
	  }
      }
    las = now;
  }
  int buc[maxn], pos[maxn], dep[maxn], dp[maxn][20];
  In void solve()
  {
    for(int i = 1; i <= cnt; ++i) ++buc[len[i]];
    for(int i = 1; i <= cnt; ++i) buc[i] += buc[i - 1];
    for(int i = 1; i <= cnt; ++i) pos[buc[len[i]]--] = i;
    for(int i = cnt; i; --i) siz[link[pos[i]]] += siz[pos[i]];
    for(int i = 1; i <= cnt; ++i)
      {
	int now = pos[i];
	dep[now] = dep[link[now]] + 1;
	dp[now][0] = link[now];
	for(int j = 1; (1 << j) <= dep[now]; ++j)
	  dp[now][j] = dp[dp[now][j - 1]][j - 1];
      }
  }
  In void query(int L, int R)
  {
    int now = a[R];
    for(int i = 18; i >=0; --i)
      if(len[dp[now][i]] >= R - L + 1) now = dp[now][i];
    ans = max(ans, 1LL * siz[now] * (R - L + 1));
  }
}S;

int p[maxn >> 1];
In void manacher(int n)
{
  int mx = 0, id;
  for(int i = 1; i <= n; ++i) //even
    {
      if(mx > i) p[i] = min(mx - i, p[(id << 1) - i - 1]);
      else p[i] = 0;
      while(i - p[i] >= 0 && s[i + p[i] + 1] == s[i - p[i]])
	{
	  ++p[i];
	  S.query(i - p[i] + 1, i + p[i]);
	}
      if(i + p[i] > mx) mx = i + p[i], id = i;
    }
  mx = 0; Mem(p, 0);
  for(int i = 1; i <= n; ++i) //odd
    {
      if(mx > i) p[i] = min(mx - i - 1, p[(id << 1) - i]);
      else p[i] = 1, S.query(i, i);
      while(i - p[i] >= 0 && s[i + p[i]] == s[i - p[i]])
	{
	  ++p[i];
	  S.query(i - p[i] + 1, i + p[i] - 1);
	}
      if(i + p[i] > mx) mx = i + p[i], id = i;
    }
}

int main()
{
  scanf("%s", s + 1);
  int n = strlen(s + 1); S.init();
  s[0] = '!'; s[n + 1] = '@';
  for(int i = 1; i <= n; ++i) S.insert(s[i] - 'a', i);
  S.solve(); manacher(n);
  write(ans), enter;
  return 0;
}
posted @ 2019-03-06 14:00  mrclr  阅读(163)  评论(0编辑  收藏  举报