CodeForces - 17E Palisection 邻接表的回文树

浅析马拉车Manacher算法(附Codeforces 17E Palisection) 引入 在字符串问题处理中,常常会遇见处理回文串的问题。那么首先就要介绍一下什么是回文串——回文串就是对于一个字符串, 阅读详情

题目:给出一个字符串要求找出多少对有相交部分的回文子串。

思路:直接求相交的不好算,我们可以用总对数减去不想交的回文串的对数。


Manacher算法:

先利用差分数组统计出以输入串的第i个字符结尾的回文串的数量sum1[i],然后令sum2[i] = sum2[i - 1] + sum1[i]作为sum1的前缀和, 那么对于中心为i半径为R[i]的回文串来说, 处在这组回文串左边的的串的贡献就是sum2中连续的一段和, 所以用sum3[i] = sum3[i- 1] + sum2[i]来表示sum2的前缀,然后这一组回文串能找到的与左边回文串的不相交的对数是 sum3[i] - sum3[i - R[i] - 1]。

具体看代码。


代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f

const LL MOD=51123987;
const int maxn=2e6+50;
char str[maxn],s[maxn<<1];//原串,修改后的串
int p[maxn<<1];//以字符s[i]为中心的最长回文子串的最左/右字符到s[i]的长度,p[i]-1就是回文串的长度
int Manacher(char *str,char *s){
    int i,j=0,len,mx=0,res=0;
    s[0]='$';
    for(i=0;str[i]!='\0';i++){
        s[2*i+1]='#';
        s[2*i+2]=str[i];
    }
    len=2*i+1;
    s[len++]='#';
    s[len]='\0';
    for(i=1;i<len;i++){
        if(mx>i)
            p[i]=min(p[2*j-i],mx-i);
        else
            p[i]=1;
        for(;s[i+p[i]]==s[i-p[i]];p[i]++);
        if(p[i]+i>mx){
            mx=p[i]+i;
            j=i;
        }
        res=max(res,p[i]-1);
    }
    return res;//返回最长回文子串的长度
}
LL d[maxn],sum[maxn];
int main(){

    int n;
    while(~scanf("%d",&n)){
        scanf("%s",str);
        Manacher(str,s);
        mm(d,0);
        for(int i=1;i<=2*n;i++){
            if(i%2==1){//回文串长度是偶数
                d[(i+1)/2]++;
                d[(i+1)/2+(p[i]-1)/2]--;
            }
            else{//回文串长度是奇数
                d[i/2]++;
                d[i/2+p[i]/2]--;
            }
        }
        LL all=0;
        sum[0]=0;
        for(int i=1;i<=n;i++){//求出以i结尾的回文串的数目
            sum[i]=sum[i-1]+d[i];
            all=all+sum[i];
        }

//        for(int i=1;i<=n;i++)
//            printf("%d %lld\n",i,sum[i]);

        if(all%2==0)
            all=(all/2)%MOD*((all-1)%MOD)%MOD;
        else
            all=((all-1)/2)%MOD*(all%MOD)%MOD;

        d[0]=0;
        for(int i=1;i<=n;i++){
            sum[i]%=MOD;
            d[i]=(d[i-1]+sum[i])%MOD;
        }

//        PP;
//        for(int i=1;i<=n;i++)
//            printf("%d %lld\n",i,d[i]);

        sum[0]=0;
        for(int i=1;i<=n;i++)
            sum[i]=(sum[i-1]+d[i])%MOD;

//        PP;
//        for(int i=1;i<=n;i++)
//            printf("%d %lld\n",i,sum[i]);

        LL ans=0;
        for(int i=1;i<=2*n;i++){
            if(i%2==1){//回文串长度是偶数
                int pos=(i+1)/2-1;
                int x=pos-1;
                int y=pos-(p[i]-1)/2-1;
                if(x<=0)
                    continue;
                y=max(0,y);
                ans=(ans+(sum[x]-sum[y])%MOD)%MOD;
                ans=(ans+MOD)%MOD;
//                printf("i=%d  pi=%d  %d  %d\n",i,p[i],x,y);
            }
            else{//回文串长度是奇数
                int pos=i/2;
                int x=pos-1;
                int y=pos-p[i]/2-1;
                if(x<0)
                    continue;
                y=max(0,y);
                ans=(ans+(sum[x]-sum[y])%MOD)%MOD;
                ans=(ans+MOD)%MOD;
//                printf("i=%d  pi=%d  %d  %d\n",i,p[i],x,y);
            }
        }
        ans=(all-ans)%MOD;
        ans=(ans+MOD)%MOD;
        printf("%lld\n",ans);
    }
    return 0;
}


回文树:

这种思路就比较清晰了,只要把next改成邻接表就行了,直接用数组会MLE


代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f

const LL MOD=51123987;
const int MAXN = 2e6+50, SIZE = 26;
struct Link_list{
    int head[MAXN],next[MAXN],ch[MAXN],id[MAXN],tot;
    void _clear(){
        mm(head,-1);
        tot=0;
    }
    void _clear(int x){
        head[x]=-1;
    }
    int _find(int x,int y){
        for(int i=head[x];i!=-1;i=next[i]){
            if(ch[i]==y)
                return id[i];
        }
        return 0;
    }
    void _insert(int x,int y,int z){
        ch[tot]=y;
        id[tot]=z;
        next[tot]=head[x];
        head[x]=tot++;
    }
};
struct Palindromic_Tree {
	Link_list nxt;
	int fail[MAXN];//fail指针
	int cnt[MAXN];//表示节点i表示的回文串的个数(建树时求出的不是完全的,最后Count()函数跑一遍以后才是正确的)
	int num[MAXN];//表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符
	int last;//指向上一个字符所在的节点,方便下一次add
	int n;//字符数组指针
	int p;//节点指针
	int NewNode(int L) {//新建节点
		nxt._clear(p);
		cnt[p] = num[p] = 0;
		len[p] = L;
		return p++;
	}
	void Init() {//初始化
	    nxt._clear();
		p = n = 0;
		NewNode(0); NewNode(-1);
		last = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判
		fail[0] = 1;
	}
	int GetFail(int x) {//和KMP一样,失配后找一个尽量最长的
		while(S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}
	int Add(int c) {
		S[++n] = c;
		int cur = GetFail(last);//通过上一个回文串找这个回文串的匹配位置
		if(!nxt._find(cur,c)) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
			int now = NewNode(len[cur] + 2);//新建节点
			fail[now] = nxt._find(GetFail(fail[cur]),c);//和AC自动机一样建立fail指针,以便失配后跳转
			nxt._insert(cur,c,now);
			num[now] = num[fail[now]] + 1;
		}
		last = nxt._find(cur,c);
		cnt[last]++;
		return num[last];
	}
	void Count() {
		for(int i = p - 1; i >= 0; --i)
			cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
	}
}tree;
char str[MAXN];
LL sum[MAXN];
int main(){

    int n;
    while(~scanf("%d",&n)){
        scanf("%s",str+1);
        tree.Init();
        sum[0]=0;
        for(int i=1;i<=n;i++){
            sum[i]=sum[i-1]+tree.Add(str[i]-'a');
        }
        LL all=sum[n];
        if(all%2==0){
            all=(all/2)%MOD*((all-1)%MOD)%MOD;
        }
        else{
            all=((all-1)/2)%MOD*(all%MOD)%MOD;
        }
        LL ans=0;
        tree.Init();
        for(int i=n;i>=1;i--){
            LL x=tree.Add(str[i]-'a');
            ans=(ans+x*sum[i-1]%MOD)%MOD;
        }
        all=(all-ans)%MOD;
        all=(all+MOD)%MOD;
        printf("%lld\n",all);
    }
    return 0;
}


STM32 ST-LINK Utility固件烧录全流程详解与实战技巧 本文详细解析了使用STM32 ST-LINK Utility进行固件烧录的全流程与实战技巧。从硬件连接、软件安装、核心三步操作(连接、擦除、烧录)到进阶的选项字节配置与批量脚本烧录,提供了完整的避坑指南和问题排查方案,帮助开发者高效完成STM32程序下载。 阅读详情

相关推荐

用AT7456E给监控摄像头加OSD?5步搞定时间水印和Logo叠加

本文详细解析了如何使用AT7456E视频字符叠加芯片为监控系统添加专业级OSD功能,涵盖硬件设计、字符库编程和系统集成等关键步骤。通过5步操作实现时间水印和Logo叠加,提升监控系统的可用性和专业性,特别适合安防和工业视觉应用。

sea99的博客 1011

Codeforces 17E Palisection Manacher ( 或 Panlindromic Tree ) + 前缀和

题目大意: 就是现在给出一个长度为n的字符串( 1 大致思路: 继续刷manacher的论文题... 刚开始看到这个题的时候我想的是觉得可以正面做, 然后发现逗比了...因为我对于每一个以 i 位置为中心的半径为R[i]回文串只算了一次, 错以为可以树状数组弄一下...然后逗比地美国样例之后发现, 以 i 为中心的回文串有R[i]个( i 处不是 '#' 的情形下)... 然后看

Gatevin的专栏 1555

为什么AI时代的优秀组织,都在学习Meta的Pod结构?看完秒懂

当AI技术重塑产业格局,企业协作正面临前所未有的挑战——层级繁琐、跨部门内耗、迭代滞后,传统组织模式早已难以跟上技术革新的步伐。作为全球AI原生企业的标杆,Meta独创的Pod组织模式,用“小而美”的自治单元打破困局,成为AI时代高效协作的典范。本文将从定义、运作、价值到未来演进,全面拆解Pod模式,为企业组织变革提供可参考的思路与路径。德鲁克曾说:“组织的功能就是让平凡的人做平凡的事。”而Meta Pod打破这一局限,通过去中心化、指标导向、跨界融合,让优秀人才产生“化学反应”。

Kiss_AIHR的博客 1163

CodeForces 17E Palisection(manacher算法+递推)

题意:给你一个长度为n的序列,问你有多少相交的回文串。 做法:可知求相交的比较难,但是求不相交的却很简单,先用manacher算法o(n)的求出每个点的回文串最长有多长。再求出start,end数组,分别代表以i为开头的回文串有几个,以i为结尾的回文串有几个。这可以利用标记再加上前缀和求,最后要求的不相交个数是:sum( start[j] * sum(end[i]| 1 PS:因为n高达2*1

glq007的acm的奋斗 1684

数字IC设计避坑指南:为什么477条net的DRV问题无法修复?解析don‘t touch hierarchy的影响

本文深入解析了数字IC后端实现中,因“don't touch hierarchy”约束导致大量DRV违规(如477条net)无法自动修复的根本原因。文章以Cadence Innovus工具为例,详细阐述了DRV修复机制与设计保护约束的冲突原理,并提供了从日志定位、风险评估到分级解除约束的完整实战策略,帮助工程师有效解决此类设计收敛难题。

hope8的博客 288

告别路径打架!用Python实现CBS算法搞定仓库机器人调度(附完整代码)

本文详细介绍了如何使用Python实现CBS(Conflict-Based Search)算法解决仓库机器人调度中的路径冲突问题。通过多智能体路径规划(MAPF)技术,有效避免AGV在智能仓库中的碰撞和死锁,提供完整代码实现和工业场景优化技巧,帮助开发者快速构建高效的机器人调度系统。

weixin_30275415的博客 327

CodeForces 17E

CodeForces 17E 题目 17E 分析 回文树裸题 这题由于空间的限制,所以要使用邻接表的形式。。 转换一下就成了回文树裸题了。 求相交的回文串个数。。 转换:求相交的个数 === 总个数 −-− 不交的个数 求回文串的总个数:回文树可以解决 回文树可以求到 iii 的回文串个数 A[i]A[i]A[i] ,把串反着插入回文树中就得到从 iii 开始的回文串个数 B[i]B[i]...

strategist_614的博客 265

C# 实现 Modbus 通信全教程(支持 TCP 和 RTU,含完整错误处理)

环境准备:安装 NModbus4 库,明确设备参数(IP/串口、地址、数据类型);连接设备:TCP 用 TcpClient,RTU 用 SerialPort,注意超时和参数匹配;读写数据:根据数据类型调用对应方法(读保持寄存器用 ReadHoldingRegisters);错误处理:捕获连接、超时、协议等异常,针对性排查问题。掌握这些后,即可轻松实现与工业设备的 Modbus 通信,为上位机开发提供稳定的数据交互能力。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 2061

CodeForces 17E Palisection(回文树)

E. Palisection time limit per test 2 seconds memory limit per test 128 megabytes input standard input output standard output In an English class Nick had nothing to do at all, and r...

weixin_30216561的博客 198

WPF+MVVM案例实战(二十三)- 阴影效果详解

通过附加属性实现阴影效果,通过改变不同的阴影属性值达到不同阴影效果

lingshuangcanxue 559

物联网通信中NB-IoT、Cat.1、LTE-M该如何选择?

首先我们要确定我们产品的关键需求,例如:我们的设备是否对数据传输速率有较高的要求,有较高要求我们就需要选择较高传输速率的Cat.1来开发我们的产品。同时关于产品性能方面,我们对功耗、延迟、可靠性也会有所要求,例如我们的产品为移动端产品,不方便布线以及安装电源,那我们就会采用电池供电的方式,所以在选择时可以优先考虑低功耗产品,最终结合其他需求选择出合适的协议。首先,我们需求的产品是不同的,我们需要根据产品需求或项目需求来进行选择,NB-IoT、Cat.1、Cat.M都有属于自己的优势。

a1809032425的博客 1214

codeforces 17E :Palisection(回文自动机)

传送门 题意: 给字符串S,求S中所有相交的回文串的对数。 题解: 以前写过一篇manacher\text{manacher}的,不过今天我才发现回文自动机原来这么好用,manacher\text{manacher}我应该是不会再碰的了。。(除非卡空间,时间其实没什么区别)。 这份代码MLEMLE了,不过本机跑了跑应该是对的,顺便写一点回文自动机的总结。 对于回文自动机上的failfa

DZYO的博客 399

YOLOv12改进策略【Neck】| 使用CARAFE轻量级通用上采样算子

CARAFE内容感知的特征重新组合。

Limiiiing的博客 664

Codeforces 17E Palisection 【Manacher】

Codeforces 17E Palisection E. Palisection In an English class Nick had nothing to do at all, and remembered about wonderful strings called palindromes. We should remind you that a string is call...

Dream_maker的博客 384

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Codeforces Beta Round #17 E. Palisection(回文树)

E. Palisection time limit per test 2 seconds memory limit per test 128 megabytes input standard input output standard output In an English class Nick had nothing to do a

u013268685的专栏(有一种幸福叫AC,有一种期待叫AK) 1017

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

CodeForces 17E Palisection (回文树)

Palisection time limit per test2 seconds memory limit per test128 megabytes inputstandard input outputstandard output In an English class Nick had nothing to do at all, and remembered about wonder

MrBlank的ACM记事本 496

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

CodeForces - 17E - Palisection ( 回文树 )

题目链接:点击进入 题目 题意 对一个给定字符串,求有多少对回文子串有交集 思路 求出这个字符串所有回文子串的对数,然后 所有的 - 无交集的 = 有交集的。 求所有的: 顺序建立回文树,用 sum 数组,通过前缀和,来维护前 i 位总共有多少回文串。 顺序枚举每个位置,以当前位置为末尾的回文串个数 cnt 乘 当前位置之前的回文串个数 sum [ i - 1 ] ,加上当前位置自身的贡献 cnt * ( cnt -1 ) / 2 ,累加答案 ans1 。 求无交集的: 逆序建立回文树,逆序枚举每个

Cosmic_Tree的博客 307

Codeforces Beta Round #17 E - Palisection 回文树写法

这一题很卡空间,如果用回文数的话。 我们可以用vector代替 next数组。 省20倍空间大概。 直接算相交回文串很难。 但算不相交回文串很简单。 我们知道,用回文树,能维护出以i结尾的回文串的个数。 再倒着维护一边字符串,就知道以i开头的回文串的个数。 然后记录前缀和直接算即可。 全部的回文串个数更简单。。用cnt数组或者直接用我们的前缀和也行 可以节省空间。 ...

bjfu170203101的博客 206

Palisection CodeForces - 17E (回文树)

题意:给一个字符串,然后求相交的回文串的对数。 思路:求出总对数,然后利用num[i]求出不相交的对数减去即可。 顺便学了mod非质数下的逆元求法,要用扩展欧几里得求,但是也有限制要分母与mod互质才行 #include<bits/stdc++.h> using namespace std; #define ls rt<<1 #define rs (rt<&...

SEIZE THE DAY 254

CodeForces 17E Palisection (回文自动机)

题意:求字符串所有回文子串相交对数。 题解:回文自动机 要求相交对数,那么先求出对数的总数,然后减去不相交的对数就好了。 sum[]sum[]sum[]:从iii开始往后的回文子串个数。从后往前更新。 add()add()add()返回值:以iii结尾的回文子串个数。 总对数−Σ(sum()∗add())- Σ(sum()*add())−Σ(sum()∗add()) 就是答案了。 然后就是nex...

gongyuandaye的博客 272

CodeForces - 17E E. Palisection(回文树)

In an English class Nick had nothing to do at all, and remembered about wonderful strings called palindromes. We should remind you that a string is called a palindrome if it can be read the same way b...

Cymbals的博客 410

Codeforces 17E Palisection 回文树

题意 给出一个长度为n的字符串S,问S有多少个回文子串存在相交关系。 n&lt;=2000000 分析 正难则反,考虑如何求不相交的回文子串数量。 通过回文树求出f[i]表示以i为结尾的回文子串数量,g[i]表示以i为开头的回文子串数量。 然后答案就是∑ni=1f[i]∗∑nj=i+1g[j]∑i=1nf[i]∗∑j=i+1ng[j]\sum_{i=1}^nf[i]*\sum_{j...

beginend 365

F - Palisection CodeForces - 17E

In an English class Nick had nothing to do at all, and remembered about wonderful strings called palindromes. We should remind you that a string is called a palindrome if it can be read the same way ...

qq_43690454的博客 167

Palisection(Codeforces Beta Round #17E+回文树)

题目链接 传送门 题意 给你一个串串,问你有多少对回文串相交。 思路 由于正着做不太好算答案,那么我们考虑用总的回文对数减去不相交的回文对数。 而不相交的回文对数可以通过计算以\(i\)为右端点的回文串的个数\(\times\)以\(i+1,i+2\dots,n\)为左端点的回文串的个数计算得到。 以\(i\)为右端点的回文串的个数可以直接用回文树\(O(n)\)求出来,以\(i\)为左...

di6499的博客 132

CodeForces - 17E Palisection (回文自动机)

n an English class Nick had nothing to do at all, and remembered about wonderful strings called palindromes. We should remind you that a string is called a palindrome if it can be read the same way b...

Top_Spirit的博客 278
上一篇: bzoj2160 拉拉队排练
下一篇: bzoj2565 最长双回文串
我不会额
博客等级 码龄11年 2粉丝 173原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值