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的更多相关文章
随机推荐
- Microsoft Visual C++ 14.0 is required
building 'twisted.test.raiser' extensionerror: Microsoft Visual C++ 14.0 is required. Get it with &q ...
- 20190416 OSX系统使用VMware Fusion安装CentOS7踩的那些坑
一.创建虚拟机 (1)在虚拟机资源库中点击[+添加]按钮,选择“新建...”选项 (2)选择创建自定义虚拟机 (3)选择系统类型为CentOS (4)选择虚拟磁盘类型 (5)选择虚拟机存储位置:点击[ ...
- spring中排除某个类
在spring中可能需要排除某个类,做法是在spring配置文件中加入如下配置 <context:component-scan base-package="com.ias" ...
- ECharts概念学习系列之ECharts是什么?
不多说,直接上干货! http://echarts.baidu.com/index.html http://echarts.baidu.com/echarts2/index.html 开源的EChar ...
- python-单链表的实现
#!/usr/bin/python class Node(object): def __init__(self,value,next=None): self.value,self.next=value ...
- WPF 使用依赖属性自定义控件
使用依赖属性自定义控件,依赖属性必须定义在自定义控件中,不能定义在其他文件中 一.先实现一个类继承你要复写的类 using System; using System.Collections.Gener ...
- IE的if条件判断
我们经常需要编写兼容代码,特别是万恶的IE. 特此记录下IE提供的if判断: <!--[if IE 5]> 仅IE5.0与IE5.5可以识别 <![endif]--> < ...
- Java将Excel的列数以字母表示的字符串转换成数字表示
我们知道,在 Excel 中,行数用数字表示,而列数是用字母表示的(如下图所示),有时候需要把它转换成数字来使用,或者把数字转换成字母.(例如使用POI操作Excel) 下面是转换代码,用来进行字母和 ...
- text-align真的只是让文本居中吗?
很多教程上说text-align属性只是让文本水平居中.但text-align的功能远不止如此. 对于具有文本类属性的元素,text-align属性也可以使其水平居中显示. 具有文本类属性的元素有:行 ...
- [转]Grunt 新手一日入门
本文转自:http://yujiangshui.com/grunt-basic-tutorial/ 当时学习 Grunt 的时候,真是很头疼.分了两个时间段,学习了两次才硬啃下来,之后才能用在项目中. ...