[BZOJ 3676][APIO 2014]回文串


考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。 


Manacher+SAM

注意clone的复制的元素不能包括size。


坑:PAM



#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#define maxn 600010
using namespace std;

char s[maxn];
int n;
long long ans;

struct Node{int len, link, nxt[26], size;}st[maxn];

int root, size, last;

void init(){
	root = size = last = 0;
	st[root].len = 0;
	st[root].link = -1;
}

int anc[maxn][20], pos[maxn];

void Extend(char ch, int part){
	int p, cur = ++ size, c = ch - 'a';
	st[cur].len = st[last].len + 1;
	st[cur].size = 1;
	pos[part] = cur;
	for(p = last; ~p && !st[p].nxt[c]; p = st[p].link)
	    st[p].nxt[c] = cur;
	pos[part] = cur;
	st[cur].size = 1;
	if(p == -1)
	    st[cur].link = root;
	else{
		int q = st[p].nxt[c];
		if(st[q].len == st[p].len + 1)
		    st[cur].link = q;
		else{
			int clone = ++ size;
			st[clone] = st[q];
			st[clone].len = st[p].len + 1;
			st[clone].size = 0;
			for(; ~p && st[p].nxt[c] == q; p = st[p].link)
			    st[p].nxt[c] = clone;
			st[q].link = st[cur].link = clone;
		}
	}
	last = cur;
}

int t[maxn], w[maxn];

void build(){
	memset(anc, -1, sizeof anc);
	for(int i = 1; i <= size; i ++)
	    anc[i][0] = st[i].link;
	for(int j = 1; 1 << j <= size; j ++)
	    for(int i = 1; i <= size; i ++){
			int a = anc[i][j - 1];
			if(~a)anc[i][j] = anc[a][j - 1];
	    }

	for(int i = 1; i <= size; i ++)
		w[st[i].len] ++;
    for(int i = 1; i <= size; i ++)
		w[i] += w[i - 1];
	//for(int i = 1; i <= size; i ++)
	for(int i = size; i >= 1; i --)
		t[w[st[i].len] --] = i;
	for(int i = size; i; i --)
		st[st[t[i]].link].size += st[t[i]].size;
}

void update(int l, int r){
	int t = pos[r];
	for(int i = 18; i >= 0; i --){
		if(~anc[t][i]){
			int to = anc[t][i];
			if(st[to].len >= r - l + 1)
			    t = to;
		}
	}
	ans = max(ans, 1ll * st[t].size * (r - l + 1));
}

int r[maxn];

void solve(){
	s[0] = '*';
	s[n + 1] = '#';
	init();
	for(int i = 1; i <= n; i ++)
	    Extend(s[i], i);
	build();

	int mx = 0, p = 0;
	for(int i = 1; i <= n; i ++){
		if(i < mx)r[i] = min(r[2 * p - i - 1], mx - i);
		else r[i] = 0;
		while(s[i + r[i] + 1] == s[i - r[i]]){
            r[i] ++;
			update(i - r[i] + 1, i + r[i]);
		}
		if(r[i] + i > mx)mx = r[i] + i, p = i;
	}

	mx = 0, p = 0;
	for(int i = 1; i <= n; i ++){
		if(i < mx){r[i] = min(r[2 * p - i], mx - i - 1);}
		else {r[i] = 1;update(i, i);}
		while(s[i + r[i]] == s[i - r[i]]){
            r[i] ++;
			update(i - r[i] + 1, i + r[i] - 1);
		}
		if(r[i] + i > mx)mx = r[i] + i, p = i;
	}

	printf("%lld\n", ans);
}

int main(){
	scanf("%s", s + 1);
	n = strlen(s + 1);
	solve();
	return 0;
}

       
       
      
      
     
     
    
    

[upd:PAM]

#include 
    
    
     
     

#define maxn 300010
using namespace std;


int n, s[maxn];

char str[maxn];

struct Node{
	int fail, nxt[26], len;
	void clear(){
		memset(nxt, 0, sizeof nxt);
		len = fail = 0;
	}
}st[maxn];

int last, size, len;

void init(){
	last = len = 0;
	st[0].clear(), st[1].clear();
	st[0].len = 0, st[1].len = -1;
	st[0].fail = st[1].fail = 1;
	s[0] = -1; size = 1;
}

int get_fail(int x){
	while(s[len-st[x].len-1] != s[len])
	    x = st[x].fail;
	return x;
}

int sz[maxn];

void Extend(int c){
	s[++ len] = c;
	int cur = get_fail(last);
	if(!st[cur].nxt[c]){
		int now = ++ size;st[now].clear();
		st[now].len = st[cur].len + 2;
		st[now].fail = st[get_fail(st[cur].fail)].nxt[c];
		st[cur].nxt[c] = now;
	}
	last = st[cur].nxt[c];
	sz[last] ++;
}

void solve(){
	long long ans = 0;
	for(int i = size; i; i --){
		sz[st[i].fail] += sz[i];
		ans = max(ans, (long long)sz[i] * st[i].len);
	}
	printf("%lld\n", ans);
}

int main(){
	init();
	scanf("%s", str+1);
	n = strlen(str+1);
	for(int i = 1; i <= n; i ++)
	    Extend(str[i]-'a');
	solve();
	return 0;
}

    
    



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值