题面:洛谷

题解:

  因为对于原串的每个长度不一定等于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]优秀的拆分 后缀数组的更多相关文章

  1. BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)

    BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...

  2. UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...

  3. BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组

    我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和. 首先枚举循环节的长度L.即$\mid (A) \mid=L$ 然后肯定会经过s[i]和[i+L]至少两个点. 然后我们可以 ...

  4. [NOI2016]优秀的拆分(SA数组)

    [NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...

  5. UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]

    #219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...

  6. NOI 2016 优秀的拆分 (后缀数组+差分)

    题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...

  7. luogu1117 优秀的拆分 (后缀数组)

    考虑分别计算每个位置作为AA的末尾或者BB的开头的个数 最后乘一乘就是答案 据说是套路的计算AA的方法: 首先枚举A的长度L,然后每L个字符当做一个关键点,这样的话,一个AA包含且只包含相邻两个关键点 ...

  8. [NOI2016]优秀的拆分&&BZOJ2119股市的预测

    [NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...

  9. luogu1117 [NOI2016]优秀的拆分

    luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...

随机推荐

  1. jsp命名规则

    jsp也用驼峰规则命名即可,不要使用下划线,否则在tomcat中容易出现解析错误

  2. 九、Django之ORM

    一.ORM概述 用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作. 到目前为止,当我们的程序涉及到数据库相关操作时,一般操作流 ...

  3. Python 3 利用 Dlib 实现摄像头人脸检测特征点标定

    0. 引言 利用 Python 开发,借助 Dlib 库捕获摄像头中的人脸,进行实时人脸 68 个特征点标定: 支持多张人脸: 有截图功能: 图 1 工程效果示例( gif ) 图 2 工程效果示例( ...

  4. Linux内核学习笔记(3)-- 进程的创建和终结

    一. 进程创建: Unix 下的进程创建很特别,与许多其他操作系统不同,它分两步操作来创建和执行进程: fork() 和 exec() .首先,fork() 通过拷贝当前进程创建一个子进程:然后,ex ...

  5. 2014-2015 ACM-ICPC, NEERC, Eastern Subregional Contest Problem G. The Debut Album

    题目来源:http://codeforces.com/group/aUVPeyEnI2/contest/229669 时间限制:1s 空间限制:64MB 题目大意:给定n,a,b的值 求一个长度为n的 ...

  6. ZOJ 3946 Highway Project 贪心+最短路

    题目链接: http://www.icpc.moe/onlinejudge/showProblem.do?problemCode=3946 题解: 用dijkstra跑单元最短路径,如果对于顶点v,存 ...

  7. Ubuntu系统升级内核方法

    一.查看内核版本 $ uname-sr //查看内核版本 二.去Ubuntu网站http://kernel.ubuntu.com/~kernel-ppa/mainline/下载所需版本的deb文件 w ...

  8. BETA阶段冲刺

    1.介绍小组新加入的成员,Ta担任的角色 新成员 担任工作 江鹭涛 前端设计 2.讨论是否需要更换团队的PM 不需要,上阶段配合不错,这阶段继续努力 3.下一阶段需要改进完善的功能 服务器并发处理,界 ...

  9. 软工网络15个人作业4--alpha阶段个人总结

    一.个人总结 自我评价表 类别 具体技能和面试问题 现在的回答 毕业找工作 语言 最拿手的语言之一,代码量是多少 java,代码量大概两三千行吧 语言 最拿手的语言之二,代码量是多少 python,代 ...

  10. JavaScript与OC的交互-WebViewJavascriptBridge

    WebViewJavascriptBridge实现了在使用UIWebView时JS与ios 的Objective-C nativecode之间的互相调用, 支持的功能有消息发送.接收.消息处理器的注册 ...