POJ 3376 Finding Palindromes (tire树+扩展kmp)
很不错的一个题(注意string会超时)
题意:给你n串字符串,问你两两匹配形成n*n串字符串中有多少个回文串
题解:我们首先需要想到多串字符串存储需要trie树(关键),然后我们正序插入倒序匹配就可以O(len)找到回文串个数了。
但是如果每次直接查询到结尾的话会漏掉两种情况
如: 1:a 与 ba 匹配(使用的是ab去匹配a)
2:ab 与 a 匹配
对于2这种情况我们需要在trie树记录每个字符串每个后缀可以形成回文串的个数(建树时就维护),对于1我们则需在匹配时看查询串每个后缀是否形成回文串。
扩展KMP:对于字符串str1的每一位与str2的最长前缀匹配记为ntand。
首先求得str1对于自己的每一位的“ntand”记为nnext,接着根据nnext求得ntand(两个函数想法与写法几乎一致)。
求nnext:我们首先暴力匹配第1个,接着根据字符串那面求得的匹配信息无回溯的求下一位
至于寻找每个后缀是否形成回文串我们使用扩展KMP的字符串正序和倒序的匹配长度来计算。
具体看代码
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=<<;
const double Pi=acos(-1.0);
const int Mod=1e9+;
const int Max=;
struct node
{
int next[];
int coun,npd;//此处结束的单词个数 接下来是回文的单词个数
void init()
{
memset(next,,sizeof(next));
coun=npd=;
}
} trie[Max];
int nnext[Max],ntand[Max];
void Exnext(int len,char *str)//扩展KMP求next数组:是数组此对应位置与开头的最长公共前缀
{
nnext[]=len;
nnext[]=;
for(int i=; i+<len&&str[i]==str[i+]; ++i) //暴力匹配第二位
nnext[]++;
int k=;//最远匹配的下标
int p,l,j;
for(int i=; i<len; ++i)
{
p=k+nnext[k]-;
l=nnext[i-k];
if(i+l<=p)//可以画图来看,一定是匹配的
nnext[i]=l;
else
{
j=p-i+;//不确定的位置
if(j<)
j=;
while(i+j<len&&str[j]==str[i+j])//可以无回溯的暴力匹配
++j;
nnext[i]=j;
k=i;
}
}
return;
}
void Extand(int len,char *str1,char *str2)//两字符串的最长公共前缀
{
Exnext(len,str1);//正序的每一位和倒序相匹配 求辅助数组
ntand[]=;//之后与辅助数组一样
for(int i=; i<len&&str1[i]==str2[i]; ++i)
ntand[]++;
int k=;
int l,p,j;
for(int i=; i<len; ++i)
{
p=k+ntand[k]-;
l=nnext[i-k];
if(i+l<=p)
ntand[i]=l;
else
{
j=p-i+;
if(j<)
j=;
while(i+j<len&&str1[j]==str2[i+j])
++j;
ntand[i]=j;
k=i;
}
}
return ;
}
char str1[Max],str2[Max];
int len[Max],tot;
void Insert(int j,char *str)//trie树插入
{
int now=,mpos;
for(int i=; i<len[j]; ++i)
{
mpos=str[i]-'a';
if(!trie[now].next[mpos])
{
trie[now].next[mpos]=++tot;
trie[tot].init();
}
now=trie[now].next[mpos];
if(i<len[j]-&&ntand[i+]==len[j]-i-)//此位置之后是回文串
{
trie[now].npd++;
} }
trie[now].coun++;
return;
}
char str[Max];
ll Search(int j)
{
ll ans=0ll;
int now=,mpos;
for(int i=; i<len[j]; ++i)
{
mpos=str2[i]-'a';//倒序
if(!trie[now].next[mpos])
return ans;
now=trie[now].next[mpos];
if(i<len[j]-&&ntand[i+]==len[j]-i-)//此位置之后是回文串
ans+=(ll)trie[now].coun;
}
return ans+(ll)trie[now].coun+(ll)trie[now].npd;//匹配结束
}
int main()
{
int n;
while(~scanf("%d",&n))
{
tot=;
trie[tot].init();
int sum=;
for(int i=; i<n; ++i)
{
scanf("%d %s",&len[i],str+sum);
int coun=;
for(int j=len[i]-;j>=;--j) //倒序
str2[coun++]=str[j+sum];
for(int j=; j<len[i]; ++j)
str1[j]=str[j+sum];
str1[len[i]]=str2[len[i]]='\0';
Extand(len[i],str2,str1);
Insert(i,str1);//正序来插入
sum+=len[i];
}
ll ans=0ll;
sum=;
for(int i=; i<n; ++i)
{
for(int j=; j<len[i]; ++j)
{
str1[j]=str[j+sum];
str2[j]=str[len[i]-j-+sum];
}
str1[len[i]]=str2[len[i]]='\0';
Extand(len[i],str1,str2);
ans+=Search(i);//倒序查询
sum+=len[i];
}
printf("%lld\n",ans);
}
return ;
}
POJ 3376 Finding Palindromes (tire树+扩展kmp)的更多相关文章
- POJ - 3376 Finding Palindromes(拓展kmp+trie)
传送门:POJ - 3376 题意:给你n个字符串,两两结合,问有多少个是回文的: 题解:这个题真的恶心,我直接经历了5种错误类型 : ) ... 因为卡内存,所以又把字典树改成了指针版本的. 字符串 ...
- poj 3376 Finding Palindromes
Finding Palindromes http://poj.org/problem?id=3376 Time Limit: 10000MS Memory Limit: 262144K ...
- POJ 3376 Finding Palindromes(扩展kmp+trie)
题目链接:http://poj.org/problem?id=3376 题意:给你n个字符串m1.m2.m3...mn 求S = mimj(1=<i,j<=n)是回文串的数量 思路:我们考 ...
- POJ 3376 Finding Palindromes EX-KMP+字典树
题意: 给你n个串串,每个串串可以选择和n个字符串拼接(可以自己和自己拼接),问有多少个拼接后的字符串是回文. 所有的串串长度不超过2e6: 题解: 这题由于是在POJ上,所以string也用不了,会 ...
- POJ - 3376 Finding Palindromes manacher+字典树
题意 给n个字符串,两两拼接,问拼接后的\(n\times n\)个字符串中有多少个回文串. 分析 将所有正串插入字典树中,马拉车跑出所有串哪些前缀和后缀为回文串,记录位置,用反串去字典树中查询,两字 ...
- POJ 3376 Finding Palindromes(manacher求前后缀回文串+trie)
题目链接:http://poj.org/problem?id=3376 题目大意:给你n个字符串,这n个字符串可以两两组合形成n*n个字符串,求这些字符串中有几个是回文串. 解题思路:思路参考了这里: ...
- Kuangbin 带你飞 KMP扩展KMP Manacher
首先是几份模版 KMP void kmp_pre(char x[],int m,int fail[]) { int i,j; j = fail[] = -; i = ; while (i < m ...
- POJ3376 Finding Palindromes —— 扩展KMP + Trie树
题目链接:https://vjudge.net/problem/POJ-3376 Finding Palindromes Time Limit: 10000MS Memory Limit: 262 ...
- KMP+Tire树(模板)
\(\color{Red}{KMP板子}\) #include <bits/stdc++.h> using namespace std; const int maxn=1e6+9; int ...
随机推荐
- unison+inotify 同步web代码并排除指定目录不同步
unison + inotify 实现web 数据双向同步 unison 是一款跨平台的文件同步对象,不仅支撑本地对本地同步,也支持通过SSH,RSH和Socket 等网络协议进行同步.unis ...
- C++ 基础知识回顾(string基础、智能指针、迭代器、容器类)
[1] string基础 [1.1] string 的构造 #include <iostream> #include <string> int main() { using n ...
- VMware Workstation 虚拟机纯 Linux 终端如何安装 VMware Tools ?
VMware Workstation 虚拟机纯 Linux 终端如何安装 VMware Tools ? 1.首先在虚拟机设置里面设置一个共享文件夹 2.在虚拟机菜单栏中选择 VMware Tools ...
- linux下tcpdump命令的使用
一般情况下linux系统会自带tcpdump工具,如果系统没有安装,直接用命令安装就行了. 安装命令:yum install -y tcpdump 查看安装版本命令:tcpdump --help 查看 ...
- 理解java注解
@是java注解,即annotation. 注解功能可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件. Java注解是附加在代码中的一些元信息,用于一些工具在编译 ...
- 001-maven下载jar后缀为lastUpdated问题
问题简述 Maven在下载仓库中找不到相应资源时,网络中断等,会生成一个.lastUpdated为后缀的文件.如果这个文件存在,那么即使换一个有资源的仓库后,Maven依然不会去下载新资源. 解决方案 ...
- 笔画宽度变化(C++和matlab算法)
最近一直在看工作方面的书籍,把论文的事情搁置了,之前承诺的贴代码的事一直拖.现在把代码整理发上来,只有核心部分的,都不是我写的,我是网上整理下载的,matlab代码的效果比较差. 全部文件网盘下载地址 ...
- Django序列化
一.Django序列化 1.序列化应用场景 1.关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,由于httpresponse只能返回字符串或者是字节,而从数据库 ...
- C# 面向对象三大特性:封装、继承、多态
面向对象有封装.继承.多态这三个特性,面向对象编程按照现实世界的特点来管理复杂的事物,把它们抽象为对象,具有自己的状态和行为,通过对消息的反应来完成任务.这种编程方法提供了非常强大的多样性,大大增加了 ...
- 曾经跳过的坑----js截取字符串substr与substring 和 trim
不废话直接代码.自己理解...... > "abcdefg".substring(1,6)> "bcdef"> "abcdefg&q ...