基本的回文串练习

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊我好激动啊!!!!!
我竟然A了!!!!!

好吧,其实这是一道模板题。(要用manacher来做(其实我觉得这算法名挺好记))

首先,我们发现回文串的长度有奇偶性,为了方便计算,我们在原串上每两个字符中间添加一个另外的字符,使原串变为奇数串。我们又发现,由于这道题玄学的 数据,0号位和n*2+2号位必须有其他的乱七八糟的值(不然会多算)。

先上马吧!

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int h = 1202;
struct node {
 string ch[h];
 int cnt;
}ans[h];
char s[h];
int n, len[h << 1], t[h << 1];
string tmp;
void init() {
 for(int i = 1; i <= n * 2 + 1; i ++)
  t[i] = '#';
 t[0] = '@';
 t[n * 2 + 2] = '$';
 for(int i = 1; i <= n; i ++)
  t[i << 1] = s[i - 1];
}
void manacher() {
 init();
 int mid = -1, r = -1;
 n <<= 1;
 n ++;
 len[0] = (1 << 30) - 1;
 for(int i = 1; i <= n; i ++) {
  len[i] = (i <= r) ? min(r - i + 1, len[mid * 2 - i]) : 1;
  if(t[i - len[i] + 1] == '#' && len[i] > 2 && t[i - len[i] + 1] == t[i + len[i] - 1]) {
   ans[len[i] - 2].cnt ++;
   for(int j = i - len[i] + 1; j < i + len[i]; j ++)
    if(t[j] != '#')
     ans[len[i] - 2].ch[ans[len[i] - 2].cnt] += t[j];
  }
  while(t[i - len[i]] == t[i + len[i]]) {
   if(len[i] >= 2 && t[i - len[i]] == '#') {
    ans[len[i] - 1].cnt ++;
    for(int j = i - len[i]; j <= i + len[i]; j ++)
     if(t[j] != '#')
      ans[len[i] - 1].ch[ans[len[i] - 1].cnt] += t[j];
   }
   len[i] ++;
  }
  if(i + len[i] - 1 >= r) {
   r = i + len[i] - 1;
   mid = i;
  }
 }
}
int main() {
 cin.getline(s, h);
 n = strlen(s);
 manacher();
 for(int i = 1; i <= 1200; i ++) {
  if(ans[i].cnt == 0)
   continue;
  for(int j = 1; j <= ans[i].cnt; j ++)
   cout << ans[i].ch[j] << endl;
 }
 return 0;
}

让我们好好研读这份代码:
mid表示以mid为中心向外延伸的字串,r表示中心为mid的最长回文串的右边界,len数组存储中心为i的最长回文串的半径(包含点i)。

继续看看这个递推式:
首先我们知道,i是一定严格大于mid的。所以如果i<r,len[mid * 2 - 1]一定等于len[i]。先别急着点头,如果len[mid * 2 - 1]所代表的回文串左边界超出了l(我们定义l为中心为mid的最长回文串的左边界),那么len[mid * 2 - 1]就不一定等于len[i]了。所以我们需要用另一个条件来限制:r-i+1即i与r的距离。

至于函数最后的赋值,大于等于和大于都没毛病。(请手推)

另外,我那清奇的码风得归功于一组数据:

111
ans:
11
11
111

好了,我已经心力交瘁了,拜拜!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值