2018 ICPC 南京 全文见:https://blog.csdn.net/qq_43461168/article/details/112796538

M. Mediocre String Problem

题意:给定两个串s和t。三元组(i,j,k)表示 s[i-j] + t[0-k],有多少个三元组使得结果串是回文串。简单来说就是在s中找一个子串 ,与t的前缀连起来变成回文串。求方案数。

!思路参考:https://www.cnblogs.com/luowentao/p/10332309.html

思路1:个人觉得思路2更好理解得多。可以先看思路2。把s[i-j] + t[0-k] 拆成三部分看。 s1 + s2 + t1。其中s2不为空。因为题目要求 s1+s2 > t1。s1 = reverse(t1)。那么就要求s2为回文串。也就是要找出 s串的所有回文。然后枚举s1的左端点。回文串可以用Manacher求出来(也还有很多别的方法,回文自动机,字符串哈希)。然后要找s1 == reverse(t1)这个东西。 因为t1就是t的前缀,而s1可以看成是s串的某个后缀的前缀。也就是和t求LCP。如果把s串倒过来。 然后再和t跑一遍EXKMP。刚好就可以求出所有的LCP了。然后就是组合回文串和LCP。遍历一遍原串。以每一个位置i,当成是(i,j,k)里面的j。然后去枚举k。其实枚举k和枚举i是一样的。只需要枚举一个。那其实也就是枚举LCP。如果线性的去枚举。那肯定超时了。因为刚刚已经计算过所有位置的LCP。那么对LCP求一个前缀和。然后枚举的时候,只需要求 i 点 回文半径+1 内的所有LCP之和,就相当于枚举了所有的s2 与 s1。就可以求出答案了。难点是下标的计算。

AC代码1:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 998244353;
const int N = 5e6+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N],c[N];const int maxn=3e6+9;   //字符串长度最大值
int nex[maxn],ex[maxn]; //ex数组即为extend数组
//预处理计算nex数组
void GETNEXT(char *str)
{int i=0,j,po,len=strlen(str);nex[0]=len;//初始化nex[0]while(str[i]==str[i+1]&&i+1<len)//计算nex[1]i++;nex[1]=i;po=1;//初始化po的位置for(i=2;i<len;i++){if(nex[i-po]+i<nex[po]+po)//第一种情况,可以直接得到nex[i]的值nex[i]=nex[i-po];else//第二种情况,要继续匹配才能得到nex[i]的值{j=nex[po]+po-i;if(j<0)j=0;//如果i>po+nex[po],则要从头开始匹配while(i+j<len&&str[j]==str[j+i])//计算nex[i]j++;nex[i]=j;po=i;//更新po的位置}}
}
//计算extend数组
void EXKMP(char *s1,char *s2)
{int i=0,j,po,len=strlen(s1),l2=strlen(s2);GETNEXT(s2);//计算子串的nex数组while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0]i++;ex[0]=i;po=0;//初始化po的位置for(i=1;i<len;i++){if(nex[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值ex[i]=nex[i-po];else//第二种情况,要继续匹配才能得到ex[i]的值{j=ex[po]+po-i;if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]j++;ex[i]=j;po=i;//更新po的位置}}
}char Ma[maxn*2];    // #号填充原串
int Mp[maxn*2];     // 回文半径
void Manacher(char s[],int len){int l=0;Ma[l++]='$';Ma[l++]='#';for(int i=0;i<len;i++){Ma[l++]=s[i];Ma[l++]='#';}Ma[l]=0;int mx=0,id=0;      // mx 最右回文右边界for(int i=0;i<l;i++){Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;if(i+Mp[i]>mx){mx=i+Mp[i];id=i;}}
}int sum[N];int cal(int l,int r){if(l > r) return 0;if(l <= 0) return sum[r];return sum[r]-sum[l-1];
}
char s1[N],s2[N];signed main(){cin>>s1>>s2;int len1 = strlen(s1);Manacher(s1,len1);reverse(s1,s1+len1);EXKMP(s1,s2);reverse(ex,ex+len1);sum[0] = ex[0];for(int i = 1; i < len1 ; i ++) sum[i] = sum[i-1]+ex[i];int res = 0;for(int i = 2 ; i < 2*len1 + 3 ; i ++){int now = Mp[i]-1;          // 回文半径if(now <= 0) continue;if(now%2 == 1){             // 奇数长度int center = (i-2)/2;   // 计算回文中心int r = center-1;int l = center-Mp[i]/2;res += cal(l,r);}else{int center = (i-2-1)/2;int r = center-1;int l = center-now/2;res += cal(l,r);}}cout<<res<<endl;return 0;
}

思路2:第一步同思路1,把s[i-j]分成s1 和 s2 去枚举。不过这次是。固定 s1 的右端点。也是就说 s[i-j] 分成 s[i-p] + s[p+1-j] ,枚举这个p。p固定之后。i,j怎么枚举呢。i其实就是LCP的最大长度。也就是 EX[p]。因为每个长度都可以贡献一次嘛。而j就简单了。 j的个数 其实就是 以p+1为起点的回文串的个数。顺着求不是很好求。 但是如果把他倒过来看。也就是把s倒过来算。 那就是以p+1为终点的回文串的个数。也就是回文后缀的个数。这个就是PAM回文树求的东西。因为fail指针就是一个回文后缀。那么fail指针的高度。就是回文后缀的个数。也就是说在插入过程中。记录这个 height[p+1] 就是 以p+1为起点的回文串的个数。有点绕。 总之就是 把s串倒过来。 然后依次插入 回文树。 模板的num[i] 记录的就是回文后缀的个数。求完之后。再把他 num数组倒回来。就行了。然后求解答案的时候。枚举到 p,贡献就是 ex[p]*num[p+1]。

AC代码2:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 998244353;
const int N = 2e6+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N],c[N];
int mark[N];char s[N];struct PAM{/**len[u] : u 节点代表回文串的长度。fa[u] : u 节点代表回文串的最长回文后缀代表的节点。tran[u][c] : 转移函数,表示在 u 代表的回文串的两端加上字符 c 之后的回文串。num[u] : 代表 u 节点代表回文串的回文后缀个数。L[i] : 代表原字符串以 i 结尾的回文后缀长度。size[u] : u 点代表的回文串的数量。**/int len[N],fa[N],size[N],num[N],tot,last,trans[N][27],L[N];int cnt[N];void init(){              // 初始化len[0]=0;fa[0]=1;len[1]=-1;fa[1]=0;tot=1;last=0;memset(trans[1],0,sizeof(trans[1]));memset(trans[0],0,sizeof(trans[0]));}int new_node(int x){       // 建立新节点int now=++tot;memset(trans[tot],0,sizeof(trans[tot]));len[now]=x;return now;}int ins(int c,int n){      // 增量法构造int u=last;while(s[n-len[u]-1]!=s[n])u=fa[u];if(trans[u][c]==0){int now=new_node(len[u]+2);int v=fa[u];while(s[n-len[v]-1]!=s[n])v=fa[v];fa[now]=trans[v][c];trans[u][c]=now;num[now]=num[fa[now]]+1;}last=trans[u][c];size[last]++;L[n]=len[last];cnt[n] = num[last];return num[last];}void build(char *s){int len = strlen(s);for(int i = 0  ; i < len ; i ++){ins(s[i]-'a',i);}}
}pam;const int maxn=3e6+9;   //字符串长度最大值
int nex[maxn],ex[maxn]; //ex数组即为extend数组
//预处理计算nex数组
void GETNEXT(char *str)
{int i=0,j,po,len=strlen(str);nex[0]=len;//初始化nex[0]while(str[i]==str[i+1]&&i+1<len)//计算nex[1]i++;nex[1]=i;po=1;//初始化po的位置for(i=2;i<len;i++){if(nex[i-po]+i<nex[po]+po)//第一种情况,可以直接得到nex[i]的值nex[i]=nex[i-po];else//第二种情况,要继续匹配才能得到nex[i]的值{j=nex[po]+po-i;if(j<0)j=0;//如果i>po+nex[po],则要从头开始匹配while(i+j<len&&str[j]==str[j+i])//计算nex[i]j++;nex[i]=j;po=i;//更新po的位置}}
}
//计算extend数组
void EXKMP(char *s1,char *s2)
{int i=0,j,po,len=strlen(s1),l2=strlen(s2);GETNEXT(s2);//计算子串的nex数组while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0]i++;ex[0]=i;po=0;//初始化po的位置for(i=1;i<len;i++){if(nex[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值ex[i]=nex[i-po];else//第二种情况,要继续匹配才能得到ex[i]的值{j=ex[po]+po-i;if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]j++;ex[i]=j;po=i;//更新po的位置}}
}
char s1[N],s2[N];
int cnt[N];signed main(){cin>>s1>>s2;int len1 = strlen(s1);int len2 = strlen(s2);strcpy(s,s1);reverse(s,s+len1);pam.init();pam.build(s);for(int i = 0 ; i < len1 ; i ++){cnt[i] = pam.cnt[len1-1-i];}reverse(s1,s1+len1);EXKMP(s1,s2);reverse(ex,ex+len1);int res = 0;for(int i = 0 ; i < len1-1 ; i ++){res += ex[i]*cnt[i+1];}cout<<res<<endl;return 0;
}

2018 ICPC 南京 M. Mediocre String Problem(ExKMP + Manacher / ExKMP+回文树)相关推荐

  1. 2018 ICPC 南京 M. Mediocre String Problem (马拉车+扩展kmp)

    题链:https://nanti.jisuanke.com/t/A2150 题意:给出两个串S,T.求S的子串接上T的前缀子串(S子串的长度要大于T前缀子串的长度.)所能组成的回文串的个数. 思路:我 ...

  2. ACM-ICPC2018南京赛区 Mediocre String Problem

    Mediocre String Problem 题解: 很容易想到将第一个串反过来,然后对于s串的每个位置可以求出t的前缀和它匹配了多少个(EXKMP 或者 二分+hash). 然后剩下的就是要处理以 ...

  3. 2018-2019 ACM-ICPC南京 M. Mediocre String Problem(SAM+PAM)

    LINK 题意 给定串 s s s和串 t t t 要求在 s s s中找一个子串 s ′ s' s′(不需要本质不同), t t t中找一个前缀 t ′ t' t′ 满足 s ′ s' s′长度大于 ...

  4. HDU - 6599 I Love Palindrome String (回文树+Manacher、回文树+hash)

    题目链接 题意 一个长度为3e5的字符串,求长度为iii的字符串满足字符是回文串而且字符串的前一半也是回文串的个数 思路 回文数求出所有的回文字符串,然后用Manacher或者Hash判断是否符合条件 ...

  5. Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢Grunt大佬的细心讲解)...

    layout: post title: Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细 ...

  6. Gym_101981M Mediocre String Problem(exkmp+manachar)

    Mediocre String Problem time limit per test:1 seconds memory limit per test:256 megabytes Problem De ...

  7. Problem M. Mediocre String Problem(Z 函数 + PAM)

    Problem M. Mediocre String Problem 给定两个串s,ts, ts,t,要求有多少不同的三元组(i,j,k)(i, j, k)(i,j,k),满足: 1≤i≤j≤∣s∣1 ...

  8. M - Mediocre String Problem( 扩展KMP + Manacher + 差分 )

    M - Mediocre String Problem( 扩展KMP + Manacher + 差分 ) 题意:给出一个串S,和一个串T. 要求 从S串中取一个子串,后面接上T串的一个前缀 组成一个结 ...

  9. Mediocre String Problem(马拉车+扩展KMP)

    文章目录 [Mediocre String Problem](https://codeforces.com/gym/101981) 题目大意 解题思路 代码 Mediocre String Probl ...

最新文章

  1. javaweb友好的删除提示语句!
  2. Java --Lamda表达式
  3. 学python用什么软件开发-5款Python程序员高频使用开发工具推荐
  4. centos7.0改变用户创建目录组权限
  5. 室内主题元素分析图_2020届室内设计专业优秀毕业设计作品展(五)
  6. SQL删除数据delete
  7. 文字处理技术:最小布局
  8. spring生命周期七个过程_想要学会Spring源码,你必知必会的BeanDefinition原理!
  9. 在构造函数中的setData和getData
  10. mysql jdbc 连接池配置
  11. Mysql的持久层_持久层(数据层,Dao层) MyBatis框架
  12. 放量十字星——黎明前的曙光还是黑暗前的夕阳
  13. python如何把csv转化为xls_python中如何将csv文件转为xls文件
  14. 小李飞刀 之 程序员版
  15. Amazon DynamoDB应用—为table指定Provisioned Throughput
  16. pandoc 转换html,使用pandoc实现markdown和latex以及html的互转
  17. 剑指 Offer 31-40
  18. 查准率/查全率/F1指标
  19. Linux网络之连接跟踪(conntrack)
  20. 卜若的代码笔记-matlab系列-matlab基础教程-第二章:静态变量

热门文章

  1. 【读书笔记】 《流畅的python》2.7-2.8 读书笔记
  2. 如何使用计算机管理找闪退原因
  3. 欧洲统一语言参考标准C1,【欧标】欧洲语言学习统一标准介绍
  4. 详解GFF转换为GTF文件
  5. 第十二周任务一(多重继承方式由这两个类派生出新类Teacher_Cadre(教师兼干部))
  6. 微信该服务器已饱满,微信官方:看看你们都许的什么愿望!把我服务器都干崩了...
  7. Win10系统下设置Go环境变量
  8. matlab机器人工具箱的使用(4)
  9. Redis—Bitmaps、HyperLogLog 、Geospatialip(地址报错(连接失败))
  10. 单细胞论文记录(part11)--ClusterMap for multi-scale clustering analysis of spatial gene expression