[NOI2016]优秀的拆分 后缀数组
题面:洛谷
题解:
因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献。
所以子串这个限制相当于是没有的。
所以我们只需要对于每个位置i求出f[i]表示以i为开头的形如BB这样的串的个数,g[i]表示以i为结尾的形如AA这样的串的个数即可。
考虑分别处理这2个数组。
我们可以枚举AA(BB)这样的串中A(B)的长度l,然后把原串每l个字符放在一个块中,在考虑统计答案。
先考虑这样一个问题:
假如固定一个串的结尾,再枚举这个串A的长度,怎样可以判断是否合法?
实际上我们只需要判断我们假定的这个AA串的开头和中间位置(结尾向前走A的长度)的LCP是否可以覆盖开头到中间即可。
然后如果我们已经把原串对于当前枚举的长度l分成了很多块,其实我们就已经可以对与每个块的开头结尾所代表的点对(i, j)判断是否可以产生贡献了。
但是怎么统计 其他没有刚好对应在某个块的开头结尾的点对 的贡献呢?
表示并没有想出来,,,但是感觉有个blog写的很好,,,
推荐一下:[BZOJ]4650 优秀的拆分(Noi2016)
以后彻底搞懂了再来填坑吧。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 301000
#define ac 602000
#define LL long long
#define rev reverse
#define mem(x) memset(x, 0, sizeof(x)) int T, n, m;
int h[ac], sa[ac], p1[ac], p2[ac], b[ac], d[ac];
int rk[ac], p[AC], t[AC], rk1[ac];
int st1[AC][], st2[AC][];
int f[AC], g[AC];
LL ans;
char s[AC]; void init()
{
for(R i = ; i <= n; i ++) f[i] = g[i] = rk[i] = ;//因为有多组数据,所以要全部清空
}//mem这么多次还不如for inline void upmax(int &a, int b){
if(b > a) a = b;
} inline int Min(int a, int b){
return (a < b) ? a : b;
} void pre()
{
scanf("%s", s + ), n = strlen(s + ), m = ;
} void ssort()
{
for(R i = ; i <= n; i ++) ++ d[p2[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = ; i <= n; i ++) b[d[p2[i]] --] = i;//给i分配d[p2[i]]的排名
for(R i = ; i <= m; i ++) d[i] = ; for(R i = ; i <= n; i ++) ++ d[p1[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = n; i; -- i) sa[d[p1[b[i]]] --] = b[i];//给b[i]分配d[p1[b[i]]]的排名
for(R i = ; i <= m; i ++) d[i] = ;
} void get_sa()
{
for(R i = ; i <= n; i ++) sa[i] = i, rk[i] = s[i];//初始化
m = ;//这个也要重置
for(R k = ; k <= n; k <<= )
{
for(R i = ; i <= n; i ++) p1[i] = rk[i], p2[i] = rk[i + k];
ssort();
int tmp = ;
rk[sa[]] = ;
for(R i = ; i <= n; i ++)
rk[sa[i]] = (p1[sa[i]] == p1[sa[i - ]] && p2[sa[i]] == p2[sa[i - ]]) ? tmp : ++ tmp;
if(tmp >= n) break;
m = tmp;//忘了,,,
}
} void build()//获取h数组
{
//memset(h, 0, sizeof(h));
for(R i = , k = ; i <= n; i ++)
{
if(k) -- k;
int j = sa[rk[i] - ];
while(s[i + k] == s[j + k]) ++ k;
h[rk[i]] = k;
}
} #define st st1
void build1()//建st1(维护LCP)
{
int tmp = , cnt = ;
memcpy(rk1, rk, sizeof(rk));
for(R i = ; i <= n; i ++)
{
st[i][] = h[i];
if(i == tmp << ) tmp <<= , ++ cnt;
p[i] = tmp, t[i] = cnt;
}
}
#undef st void build2()//建st2(维护LCS)改成st1, st2一起建了。。。。
{
for(R i = ; i <= n; i ++) st2[i][] = h[i];
int tmp = ;
for(R i = ; i <= ; i ++)
{
for(R j = ; j <= n; j ++)
{
st1[j][i] = Min(st1[j][i - ], st1[j + tmp][i - ]);
st2[j][i] = Min(st2[j][i - ], st2[j + tmp][i - ]);
}
tmp <<= ;
}
} inline void swap(int &l, int &r)
{
int x = l;
l = r, r = x;
} int get1(int l, int r)//查询串l和串r的LCP
{
l = rk1[l], r = rk1[r];
if(l > r) swap(l, r);
++ l;
int len = r - l + ;
return Min(st1[l][t[len]], st1[r - p[len] + ][t[len]]);
} int get2(int l, int r)//查询串l和串r的LCS
{//因为是翻转过来求的,所以查询要翻转一下
l = n - l + , r = n - r + ;
l = rk[l], r = rk[r];
if(l > r) swap(l, r);
++ l;
int len = r - l + ;
return Min(st2[l][t[len]], st2[r - p[len] + ][t[len]]);
} void get()
{
int lim = n << ;
for(R k = ; k < lim; k ++)//枚举A的长度
{
int maxn = , maxn2 = ;
for(R i = ; i <= n; i += k)
{
int j = i + k;//j为下一段开头
if(j > n) break;
if(i > maxn)
{
int lcp = get1(i, j), lcs = get2(i, j);
maxn = i + lcp - ;
int l = i - lcs + , r = j + lcp - * k;
if(lcp + lcs > k) ++ f[l], -- f[r + ];
}
if(i > maxn2)
{
int lcp = get2(n - i + , n - j + ), lcs = get1(n - i + , n - j + );
maxn2 = i + lcp - ;
int l = i - lcs + , r = j + lcp - * k;
if(lcp + lcs > k) ++ g[l], -- g[r + ];
}
}
}
for(R i = ; i <= n; i ++) f[i] += f[i - ], g[i] += g[i - ];
rev(g + , g + n + );
} void work()
{
ans = ;//f是开头
for(R i = ; i <= n; i += )
ans += 1LL * f[i] * g[i - ] + ((i + > n) ? : 1LL * f[i + ] * g[i]);
printf("%lld\n", ans);
} int main()
{
// freopen("in.in", "r", stdin);
scanf("%d", &T);
while(T --)
{
init();
pre();
get_sa();
build();
build1();//建st(维护LCP)
rev(s + , s + n + );//翻转
// memset(rk, 0, sizeof(rk));//因为对于单组数据而言,长度不变,所以rk不必再次清空
get_sa();
build();
build2();//建st2(维护LCS)
get();
work();
}
// fclose(stdin);
return ;
}
[NOI2016]优秀的拆分 后缀数组的更多相关文章
- BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)
BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...
- UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)
连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...
- BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组
我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和. 首先枚举循环节的长度L.即$\mid (A) \mid=L$ 然后肯定会经过s[i]和[i+L]至少两个点. 然后我们可以 ...
- [NOI2016]优秀的拆分(SA数组)
[NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...
- UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
#219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...
- NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...
- luogu1117 优秀的拆分 (后缀数组)
考虑分别计算每个位置作为AA的末尾或者BB的开头的个数 最后乘一乘就是答案 据说是套路的计算AA的方法: 首先枚举A的长度L,然后每L个字符当做一个关键点,这样的话,一个AA包含且只包含相邻两个关键点 ...
- [NOI2016]优秀的拆分&&BZOJ2119股市的预测
[NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...
- luogu1117 [NOI2016]优秀的拆分
luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...
随机推荐
- [POJ3041]Asteroids
Asteroids 好久没打过网络流相关的题了...... 题意:一个矩阵n×n,有m个东西,一次去掉一整行或一整列,问最少次数. 题解:匈牙利. 把每行变成一个点(X集合),每列变成一个点(Y集合) ...
- git remote: error: hook declined to update
提交一个项目,push的时候,报错: remote: error: File xxx.rar is MB; this exceeds Git@OSC's file size limit of 100 ...
- 模拟IDE上的run过程
看了一下老陈写的模仿JDK动态代理,从中取一部分单独扩展,模拟一下IDE上的run过程(不愧是老陈,去年写的东西我要现在才能理解) 对run过程的猜想 在点击run的过程中应该做了不少事.先编译运行r ...
- UVa 10071
简单运动学公式 v=v0+at x=v0t+1/2*a*t^2=2vt #include<stdio.h> int main() { int v, t; while((scanf(&quo ...
- java-length 、length()、size()的区别
public static void main(String[] args) { //length .length().size()的区别 //length属性 针对数组长度 String a[]={ ...
- gevent协程、select IO多路复用、socketserver模块 改造多用户FTP程序例子
原多线程版FTP程序:http://www.cnblogs.com/linzetong/p/8290378.html 只需要在原来的代码基础上稍作修改: 一.gevent协程版本 1. 导入geven ...
- Powershell按文件最后修改时间删除多余文件
Powershell按文件最后修改时间删除多余文件 1. 删除目录内多余文件,目录文件个数大于$count后,按最后修改时间倒序排列,删除最旧的文件. Sort-Object -Property La ...
- 第二次c艹作业
1,c语言实现方法:按照电梯运行方式,改变被定义为全局变量的结构体的数值. c艹实现方法:用类来存放电梯的属性,整个过程都是对类操作. 两者不同:c语言是面向过程的,整个函数里都要对电梯的参数进行修改 ...
- UVA 11270 轮廓线
题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=33787 题意: 用1*2或2*1的长条把n*m方格铺满的方案数. ...
- HTML5 <meta> 标签属性,所有meta用法
基本标签 声明文档使用的字符编码:<meta charset="utf-8" /> 声明文档的兼容模式:<meta http-equiv="X-UA-C ...