UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
#219. 【NOI2016】优秀的拆分
题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个
一开始一直想直接求,并不方便
然后看了一眼Claris的题解的第一行就有思路了
如果分开,求\(f[i]\)以i结尾AA形式子串和\(g[i]\)以i开始AA形式子串 就可以套路了
使用常用技巧,枚举\(L=|A|\),AA子串一定覆盖了两个关键点,枚举更新就行了,对于区间加可以使用差分
其实这道题很好拿95分啊,\(O(n^2)\)用哈希判断就行了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=3e4+5;
typedef long long ll;
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, Log[N]; char s[N];
namespace ST {
void build(int f[N][16], int *a) {
for(int i=1; i<=n; i++) f[i][0]=a[i];
for(int j=1; j<15; j++)
for(int i=1; i+(1<<j)-1<=n; i++)
f[i][j] = min(f[i][j-1], f[i+(1<<(j-1))][j-1]);
}
}
struct SA {
int sa[N], t1[N], t2[N], c[N], rnk[N], hei[N], f[N][16];
inline bool cmp(int *r, int a, int b, int j) {
return a+j<=n && b+j<=n && r[a]==r[b] && r[a+j]==r[b+j];
}
void build(char *s, int m) {
int *r=t1, *k=t2;
for(int i=0; i<=m; i++) c[i]=0;
for(int i=1; i<=n; i++) c[r[i]=s[i]]++;
for(int i=1; i<=m; i++) c[i] += c[i-1];
for(int i=n; i>=1; i--) sa[ c[r[i]]-- ]=i;
for(int j=1; j<=n; j<<=1) {
int p=0;
for(int i=n-j+1; i<=n; i++) k[++p]=i;
for(int i=1; i<=n; i++) if(sa[i]>j) k[++p]=sa[i]-j;
for(int i=0; i<=m; i++) c[i]=0;
for(int i=1; i<=n; i++) c[r[k[i]]]++;
for(int i=1; i<=m; i++) c[i] += c[i-1];
for(int i=n; i>=1; i--) sa[ c[r[k[i]]]-- ]=k[i];
swap(r, k); p=0; r[sa[1]]=++p;
for(int i=2; i<=n; i++) r[sa[i]] = cmp(k, sa[i], sa[i-1], j) ? p : ++p;
if(p>=n) break; m=p;
}
int now=0;
for(int i=1; i<=n; i++) rnk[sa[i]]=i;
for(int i=1; i<=n; i++) {
if(now) now--;
if(rnk[i]==1) continue;
int j=sa[rnk[i]-1];
while(i+now<=n && j+now<=n && s[i+now]==s[j+now]) now++;
hei[rnk[i]]=now;
}
ST::build(f, hei);
}
int lcp(int x, int y) {
x=rnk[x], y=rnk[y];
if(x>y) swap(x, y); x++;
int t=Log[y-x+1];
return min(f[x][t], f[y-(1<<t)+1][t]);
}
}a, b;
inline int lcp(int x, int y) {return a.lcp(x, y);}
inline int lcs(int x, int y) {return b.lcp(n-x+1, n-y+1);}
int f[N], g[N];
inline void add(int *d, int l, int r) {d[l]++; d[r+1]--;}
void solve(int L) { //printf("\nsolve %d\n",L);
for(int i=1; i+L<=n; i+=L) {
int l = i - lcs(i, i+L) + 1, r = i + L + lcp(i, i+L) - 1;
l = max(l, i-L+1); r = min(r, i+L+L-1);
l = max(l, 1); r = min(r, n);
//printf("key %d %d [%d, %d]\n", i, i+L, l, r);
if(r-l+1 < 2*L) continue;
add(f, l+2*L-1, r); add(g, l, r-2*L+1);
}
}
int main() {
freopen("in","r",stdin);
Log[1]=0; for(int i=2; i<N; i++) Log[i] = Log[i>>1]+1;
int T=read();
while(T--) {
scanf("%s", s+1); n=strlen(s+1);
a.build(s, 260); reverse(s+1, s+1+n); b.build(s, 260); reverse(s+1, s+1+n);
for(int i=1; i<=n; i++) f[i]=g[i]=0;
//for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) printf("lcs %d %d %d\n",i,j,lcs(i,j));
for(int i=1; i<=n; i++) solve(i);
ll ans=0;
for(int i=1; i<=n; i++) f[i]+=f[i-1], g[i]+=g[i-1];// printf("look %d %d %d\n",i,f[i],g[i]);
for(int i=2; i<n; i++) ans += (ll)f[i]*g[i+1];
printf("%lld\n", ans);
}
}
UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]的更多相关文章
- UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表
原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...
- [NOI2016]优秀的拆分 后缀数组
题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...
- UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)
连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...
- BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)
BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...
- BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组
我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和. 首先枚举循环节的长度L.即$\mid (A) \mid=L$ 然后肯定会经过s[i]和[i+L]至少两个点. 然后我们可以 ...
- SPOJ 687 Repeats(后缀数组+ST表)
[题目链接] http://www.spoj.com/problems/REPEATS/en/ [题目大意] 求重复次数最多的连续重复子串的长度. [题解] 考虑错位匹配,设重复部分长度为l,记s[i ...
- POJ 3693 Maximum repetition substring(后缀数组+ST表)
[题目链接] poj.org/problem?id=3693 [题目大意] 求一个串重复次数最多的连续重复子串并输出,要求字典序最小. [题解] 考虑错位匹配,设重复部分长度为l,记s[i]和s[i+ ...
- BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay
BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔 ...
- UVA10829 L-Gap Substrings(后缀数组+ST表)
后缀数组+ST表. 代填的坑. \(Code\ Below:\) #include <bits/stdc++.h> #define ll long long using namespace ...
随机推荐
- 2017广东工业大学程序设竞赛E题(倒水)
Description 一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水.接着~~CC发现瓶子实在太多了,于是他决定保留不超过K个瓶子.每次他选择两个当前含水量相同的瓶子,把一个 ...
- 换行符 '\n' 和 回车符 '\r' 的区别?
顾名思义: 换行符就是另起一新行,光标在新行的开头: 回车符就是光标回到一旧行的开头:(即光标目前所在的行为旧行) ------------------------------------------ ...
- 分布式计算框架学习笔记--hadoop工作原理
(hadoop安装方法:http://blog.csdn.net/wangjia55/article/details/53160679这里不再累述) hadoop是针对大数据设计的一个计算架构.如果你 ...
- C语言mktime()
最近在调试stm32L151单片机,因为业务需要将从RTC获取的时间转换成时间戳.转换的时候发现获取的时间一直不对.一直被两个问题困扰. 1.从RTC获取出来的月份为什么比实际月份小1? 2.转换得来 ...
- 如何查看sublime安装了哪些插件
你应该安装过package control. 那么只要这样:按ctrl+shift+p,输入package,选择list packages,就看到了. 或者直接查看Installed Packages ...
- Html5+js测试题(开发版)
------------------------------------------------ 1. 谈谈你对js闭包的理解: 使用闭包主要是为了设计私有的方法和变量.闭包的优点是可以避免全局变量的 ...
- javaScript之jQuery
一.jQuery简介 jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架).jQuery设计的宗旨 ...
- [转]linux下centos服务器安全设置
引言: 我们必须明白:最小的权限+最少的服务=最大的安全 所以,无论是配置任何服务器,我们都必须把不用的服务关闭.把系统权限设置到最小话,这样才能保证服务器最大的安全.下面是CentOS服务器安全设置 ...
- 微信跳一跳的mini辅助设计
前一段考试没时间写东西,就迟到补发一波,继电器触发触屏,arduino处理数据就行了,B站很多人做,我也来一个,个人测试数据增益为2.1左右,即 延时=距离X2.1x10 void setup() ...
- java —— 内部类
_ 普通内部类 静态内部类 局部内部类 匿名内部类 内部类 内部类是定义在另一个类中的类,定义内部类会起到的作用有以下三点: 1.内部类方法访问该类定义所在的作用域中的数据,包括私有的数据. 2.内部 ...