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最常见的200+面试题及自己梳理的答案--面试必备(一)
昨天在今日头条上看到一份所谓经常面别人的TL梳理的面试题,看着比较完善,但是,没有对应的答案,自己看着研究学习了下,顺带梳理下答案.主要包括以下模块:Java基础.容器.多线程.反射.对象拷贝.Jav ...
- SpringMvc redirect
SpringMVC redirect 核心 首先网上百度到的资源基本已经够用, 留作记录. SpringMVC-redirect重定向跳转传值 虽然这哥们也是转的, 但又没有留源地址. 因此 ... ...
- Cause: org.jetbrains.plugins.gradle.tooling.util.ModuleComponentIdentifierIm Lorg/gradle/api/artifacts/ModuleIdentifier;
今天碰到一个问题, 正常的下载gradle到本地,然后到spring官网上通过他们提供的start.spring.io创建一个demo项目, 然后在idea中打开,并配置下载的gradle到idea中 ...
- Luogu P1342 请柬 题解
差不多是Dijkstra的裸题吧... 这道题可以分为来回两个阶段. 去的时候很简单,直接用一次Dijkstra,然后统计答案. 回来的时候就有些巧妙了,虽然表面上是每个点回到起点,但是何尝不可将其看 ...
- [BZOJ 1056][HAOI2008]排名系统
传送门 \(\color{green}{solution}\) \(fhq \_treap\)模板题. 对于 \(+\) 操作,如果当前人不存在,那么直接加入;如果存在,那么先将他删除,再加入.复杂度 ...
- TP5 隐藏入口文件 index.php
找到public下的.htaccess <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews RewriteEngine ...
- 获得自己电脑的SSH公匙
关于什么是SSH请点击此"www.Baidu.com”网站了解 我这里只说怎么获取属于自己电脑的SSH公匙 本人是Win10电脑 所以相对来说简单一点 点击win ->选择设置-&g ...
- 通过MSI解压缩Cab文件
迁移自我的Github 如果只是想做类似解压缩的操作,那么可以使用如下命令行 C:\Windows\System32\expand.exe <cab file path> -F:* < ...
- FX4300超频4.7GHz
先贴出本人计算机配置: (本人cpu为fx4300,默认频率为3.8GHz) 谨记:超频有风险,很可能烧坏主板.cpu.内存等硬件,特别是增加主板电压时一定一次增加0.025V,不要一次增加太多,并且 ...
- 数据库SQLITE3初识
数据库DataBase,我们都没有接触过数据库,那么数据库是什么? 它是一个有结构的.集成的.可共享的统一管理的数据集合! 所谓有结构的,指的是数据是按一定的模型组织起来的. 简单的说,拿个箱子,用隔 ...