传送门

以前看到这题的时候觉得是道好题啊……然而今天没多久就做出来了= =(装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的更多相关文章

随机推荐

  1. 多并发编程基础 之线程程 Thried

    原贴 https://www.cnblogs.com/gbq-dog/p/10365669.html 今日要整理的内容有 1. 操作系统中线程理论 2.python中的GIL锁 3.线程在python ...

  2. P4383 [八省联考2018]林克卡特树lct

    题目链接 题意分析 一句话题意就是 : 让你选出\((k+1)\)条不相交的链 使得这些链的边权总和最大 (这些链可以是点) 我们考虑使用树形\(DP\) \(dp[i][j][0/1/2]\)表示以 ...

  3. 【NOI2018】

    总之国赛已经过了1个月了. 感谢北大当初给我的一本约救我狗命,不然国赛就要没学上了. 铜牌倒数十多名,我觉得我也是混到了一种境界. 虽然对于集训队已经失去梦想,但是,Day1全场堪称最低的21分,也是 ...

  4. 查看linux上面是否有安装redis

  5. json兼容ie8

    今天遇到一个问题,后台传递过来的json对象,在前端解析的时候用JSON.parse(result)方法不好使,查了一下是因为ie浏览器的问题.然后在网上翻了翻,找到了这个办法,可以使这个函数在ie中 ...

  6. poj3207 Ikki's Story IV - Panda's Trick 2-SAT

    题目传送门 题意:在一个圆上顺时针安放着n个点,给出m条线段连接端点,要求线段不相交,线段可以在圆内也可以在圆外,问是否可以. 思路:假设一条线段,放在圆外是A,放在园内是A',那么两条线段如果必须一 ...

  7. DA14580_583_DK_II开发板入门笔记

    本文链接:http://www.cnblogs.com/obarong/p/8521893.html 1.介绍 开发板资料 参考文件: DA1458XDK蓝牙开发板用户须知1.3.pdf DA1458 ...

  8. 【ORACLE】ID 2299494.1 安装Oracle 11g 86%报错:Error in invoking target 'agent nmhs' of makefile

    参考: ID 2299494.1 In this Document   Symptoms   Changes   Cause   Solution   References APPLIES TO: O ...

  9. 003javascript语句

    javascript语句和java差不多,注意==和===区别 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" " ...

  10. 第3章—高级装配—bean的作用域

    bean的作用域 bean的默认作用域 Spring定义了多种作用域,可以基于这些作用域创建bean,包括: 单例(Singleton):在整个应用中,只创建bean的一个实例. 原型(Prototy ...