COGS2217 papertask
以前看到这题的时候觉得是道好题啊……然而今天没多久就做出来了= =(装B
表示并没有看懂其他人写的是什么做法,感觉我的做法好奇怪……
我的做法是这样的:
首先给括号配对,不难发现所有括号串要么互不相交要么互相包含,也就是说它们形成了一个树形结构,暂且称之为括号树。
比如括号序列[[[][]][[[]][][]]]的括号树就是这样的:
每个节点的儿子就是剥掉最外层括号后的括号串,那么不难看出所有合法子串一定是某个节点的儿子顺序排列之后取连续的一段(必须是同一个节点的儿子,否则会因为最外层括号的缘故而使得子串不合法)。如果可以找到一种方法来区别本质不同的节点,那么直接把所有节点的儿子排列得到的字符串一块建一个广义后缀自动机就可以统计本质不同的子串个数了。最简单的方法就是利用哈希,然后就可以了……
注意这里的字符集可能很大,一开始觉得广义后缀自动机的复杂度是错的。后来才反应过来,所有字符串总长度只有n,因此建广义后缀自动机也还是线性的。
当然原串中连续的极长括号串也需要并在一起当作字符串插进去,这个稍微处理一下就行了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=;
const unsigned long long base=131ull;
void solve(int,int);
void insert(int*,int);
int expand(unsigned long long,int);
unsigned long long gethash(int,int);
int root,cnt=,val[maxn]={},par[maxn]={};
map<unsigned long long,int>go[maxn];
unsigned long long h[maxn],pw[maxn];
char str[maxn];
long long ans=;
int n,a[maxn],s[maxn],top,t[maxn],last=;
int main(){
freopen("papertask.in","r",stdin);
freopen("papertask.out","w",stdout);
int __size__=<<;
char *__p__=(char*)malloc(__size__)+__size__;
__asm__("movl %0, %%esp\n"::"r"(__p__));
root=++cnt;
scanf("%d",&n);
scanf("%s",str+);
pw[]=;
for(int i=;i<=n;i++)pw[i]=pw[i-]*base;
for(int i=n;i;i--)h[i]=h[i+]*base+str[i];
for(int i=;i<=n;i++){
if(str[i]=='(')s[++top]=i;
else{
if(!top)continue;
a[i]=s[top--];
a[a[i]]=i;
}
}
int x=,last=;
while(x<=n){
if(!a[x]){
x++;
continue;
}
last=x;
while(a[a[x]+])x=a[x]+;
solve(last,a[x]);
x=a[x]+;
}
printf("%lld\n",ans);
return ;
}
void solve(int l,int r){
if(l>r)return;
int cnt=,x=l;
while(x<=r){
t[cnt++]=gethash(x,a[x]-x+);
x=a[x]+;
}
insert(t,cnt);
x=l;
while(x<=r){
solve(x+,a[x]-);
x=a[x]+;
}
}
void insert(int *a,int n){
int x=root,i=;
//for(;i<n&&go[x].count(a[i]);i++)x=go[x][a[i]];
for(;i<n;i++)x=expand(a[i],x);
}
int expand(unsigned long long c,int p){
int np=++cnt;
val[np]=val[p]+;
while(p&&!go[p].count(c)){
go[p][c]=np;
p=par[p];
}
if(!p)par[np]=root;
else{
int q=go[p][c];
if(val[q]==val[p]+)par[np]=q;
else{
int nq=++cnt;
val[nq]=val[p]+;
go[nq]=go[q];
par[nq]=par[q];
par[np]=par[q]=nq;
while(p&&go[p][c]==q){
go[p][c]=nq;
p=par[p];
}
}
}
ans+=val[np]-val[par[np]];
return np;
}
inline unsigned long long gethash(int x,int l){return h[x]-h[x+l]*pw[l];}
COGS2217 papertask的更多相关文章
随机推荐
- Java多线程——同步容器类
1.同步容器类 同步容器类包括Vector和Hashtable,是早期JDK的一部分,这些类实现的方法是:将它们的状态封装起来,并对每个共有的方法进行同步,使得每个线程只有一个线程能访问它们. 1.1 ...
- QuantLib 金融计算——数学工具之随机数发生器
目录 QuantLib 金融计算--数学工具之随机数发生器 概述 伪随机数 正态分布(伪)随机数 拟随机数 HaltonRsg SobolRsg 两类随机数的收敛性比较 如果未做特别说明,文中的程序都 ...
- docker阿里云镜像加速器使用
加速器使用:加快镜像下载速度 访问www.aliyun.com: 登录之后点击”控制台“,选择“产品与服务“: 选择“容器镜像服务“: 设定密码后选择“镜像加速器”: 这里会有一个加速器地址: 在 ...
- Django中的Session--实现登录
Django中的Session--实现登录 Django Session Session Session 是什么 Session保存在服务端的键值对. 为什么要有 Session Cookie 虽然 ...
- 【第2次会议记录_2018.5.27】—— [ 算法原理 ]:手工特征提取的概念问题。(by_wanghao)
1.提取 特征点 .特征描述子 与 提取特征向量 之间的区别: (1).特征点:指的是一张图片上比较有代表性的‘位置’,提取特征点就是把图片中这些有代表性的位置给标出来. (2).特征描述子:当提取出 ...
- 2019.4.25 表格表单与HTML5 && CSS3
目录 表格 标签 属性 表格间距离 表格的内边距 表格的边框 样式 边框合并 行合并 列合并 display 表单 标签 属性 提交的网址 请求方式 input相关 扩大响应范围 字符 密码 单选框 ...
- 一次java导出pdf的经历
近期由于工作需要,需要将html代码导入到pdf中,经过了几种pdf的方案对比后发现IText是最简单和便捷的一种方式,于是乎采用了Itext. PDF生成 第一步:导入Maven依赖 <!-- ...
- TreeMap红黑树
Java TreeMap实现了SortedMap接口,也就是说会按照key的大小顺序对Map中的元素进行排序,key大小的评判可以通过其本身的自然顺序(natural ordering),也可以通过构 ...
- 深入理解java集合框架之---------HashMap集合
深入理解HaspMap死循环问题 由于在公司项目中偶尔会遇到HashMap死循环造成CPU100%,重启后问题消失,隔一段时间又会反复出现.今天在这里来仔细剖析下多线程情况下HashMap所带来的问题 ...
- Kubernetes是什么
目录 简介 主要概念: 总体结构 参考 Kubernetes概念 简介 kubernetes是一个Google开源的容器编排系统,用于自动部署,扩展和管理容器化应用程序. 随处运行:支持公有云,私有云 ...