目录

  • @description@
  • @solution@
  • @accepted code@
  • @details@


@description@

给定一个长度为 n 的仅包含'B'、'C'、'S'三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同。

input
第一行包含一个正整数 n(1<=n<=1000000),表示字符串的长度。
第二行一个长度为 n 的字符串。

output
包含一行一个正整数,即最长的满足条件的子串的长度。

sample input
9
CBBSSBCSC
sample output
6

@solution@

在这篇博客内有一个很神仙的结论:

最优解 [l, r] 一定满足 \(l \in [1, 3]\) 或 \(r \in [n-2, n]\)。

至于证明,那位博主略去了。。。

我来尝试证明一下吧:

性质 <1>: 如果 [l, r] 满足没有任意两种字符出现次数相同,则一定能找到合法区间 [l', r'] 满足 \(l' \in [l-3,l], r' \in [r,r+3]\),且 r - l + 1< r' - l' + 1。
使用反证法。不失一般性,假设 [l, r] 内三种字符的出现次数分别为 (a, b, c) 且满足 a < b < c。

分类讨论 l-1 是什么字符:
(1) [l-1, r] 对应 (a, b, c+1),始终满足 a < b < c + 1。
(2) [l-1, r] 对应 (a, b+1, c),若不满足 a < b + 1 < c,则有 b + 1 = c 成立。
(2) [l-1, r] 对应 (a+1, b, c),若不满足 a + 1 < b < c,则有 a + 1 = b 成立。

类似地对 r+1 进行分类,可以得到一个初步的结论:如果性质不成立,那么 (a, b, c) 至少要满足 a + 2 = b + 1 = c。

剩下的证明因为我很懒再加上我也不是专业的采用 dfs 枚举所有可能。用于验证的程序如下:

#include<cstdio>
int a[6], s[3];
void dfs(int x) {if( x == 6 ) {for(int i=0;i<=3;i++)for(int j=2;j<=5;j++) {if( i <= j ) {s[0] = -1, s[1] = 0, s[2] = 1;for(int k=i;k<=j;k++)s[a[k]]++;if( s[0] != s[1] && s[0] != s[2] && s[1] != s[2] )return ;}}puts("error");return ;}a[x] = 0; dfs(x + 1);a[x] = 1; dfs(x + 1);a[x] = 2; dfs(x + 1);
}
int main() {dfs(0);
}

性质 <2>:如果 [l, r] 满足只有一种字符且 r - l + 1 > 1,则一定能找到合法区间 [l', r'] 满足 l - 1 = l' 或 r + 1 = r'。
对于区间 [l-1, r],它要么是形如 (0, 0, r - l + 2),要么是形如 (0, 1, r - l + 1)。
对于区间 [l, r+1] 同理。

有了这两个性质就可以证明我们的结论了。
假如最优解 [l, r] 不满足结论,则有 l > 3, r < n - 2。由性质 <1> 或性质 <2> 可知可以通过移动它的左右端点使得区间长度变大。故它一定不是最优解。
有一些不严谨的地方,就是如果 r - l + 1 = 1 的时候。这种情况特殊讨论一下也成立。

时间复杂度 O(n)。

@accepted code@

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1000000 + 5;
inline int fun(char ch) {if( ch == 'B' ) return 0;if( ch == 'C' ) return 1;if( ch == 'S' ) return 2;
}
int n, sum[3][MAXN];
int f(int x, int l, int r) {return sum[x][r] - sum[x][l - 1];
}
bool check(int l, int r) {if( f(0, l, r) != f(1, l, r) && f(0, l, r) != f(2, l, r) && f(1, l, r) != f(2, l, r) ) return true;if( (!f(0, l, r) && !f(1, l, r)) || (!f(0, l, r) && !f(2, l, r)) || (!f(1, l, r) && !f(2, l, r)) ) return true;return false;
}
char s[MAXN];
int main() {int n, ans = 1;scanf("%d%s", &n, s + 1);for(int i=1;i<=n;i++) {sum[0][i] = sum[0][i-1];sum[1][i] = sum[1][i-1];sum[2][i] = sum[2][i-1];sum[fun(s[i])][i]++;}for(int i=1;i<=n;i++) {if( 1 <= i && check(1, i) ) ans = max(ans, i - 0);if( 2 <= i && check(2, i) ) ans = max(ans, i - 1);if( 3 <= i && check(3, i) ) ans = max(ans, i - 2);if( i <= n - 0 && check(i, n - 0) ) ans = max(ans, n - i + 1);if( i <= n - 1 && check(i, n - 1) ) ans = max(ans, n - i);if( i <= n - 2 && check(i, n - 2) ) ans = max(ans, n - i - 1);}printf("%d\n", ans);
}

@details@

当然这种神仙结论我肯定是想不到的。

所以,我选用的是另外一种方法:
只含一种字符的区间显然可以随便怎么搞。

定义 sum[i] 表示字符 i 的前缀和。如果区间 [l + 1, r] 合法,则一定有:
sum[0][r] - sum[0][l] ≠ sum[1][r] - sum[1][l]
sum[0][r] - sum[0][l] ≠ sum[2][r] - sum[2][l]
sum[1][r] - sum[1][l] ≠ sum[2][r] - sum[2][l]

变一下形:
sum[0][r] - sum[1][r] ≠ sum[0][l] - sum[1][l]
sum[0][r] - sum[2][r] ≠ sum[0][l] - sum[2][l]
sum[1][r] - sum[2][r] ≠ sum[1][l] - sum[2][l]

枚举 r,相当于找到一个最小的 l 满足上述条件。
先判断它是不是三个前缀和都不一样,如果是就有 l = 0。
接下来,对于某一个 l,不满足上述条件的 r 其实很少。我们大力分类讨论即可。
时间复杂度还是 O(n) 的,但是代码复杂度……

转载于:https://www.cnblogs.com/Tiw-Air-OAO/p/10364777.html

@bzoj - 4384@ [POI2015] Trzy wieże相关推荐

  1. 【BZOJ4384】【POI2015】Trzy wieże (……)

    题面

  2. BZOJ 3747 POI2015 Kinoman 段树

    标题效果:有m点,每个点都有一个权值.现在我们有这个m为点的长度n该序列,寻求区间,它仅出现一次在正确的点区间内值和最大 想了很久,甚至神标题,奔说是水的问题--我醉了 枚举左点 对于每个请求留点右键 ...

  3. bzoj 3750: [POI2015]Pieczęć(模拟)

    3750: [POI2015]Pieczęć Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 309  Solved: 171 [Submit][Sta ...

  4. bzoj 3749: [POI2015]Łasuchy

    3749: [POI2015]Łasuchy Time Limit: 10 Sec  Memory Limit: 64 MBSec  Special Judge Submit: 355  Solved ...

  5. [Luogu P3597] [BZOJ 4386] [POI2015]WYC

    洛谷传送门 BZOJ传送门 题目描述 给定一张nnn个点mmm条边的带权有向图,每条边的边权只可能是111,222,333中的一种.将所有可能的路径按路径长度排序,请输出第kkk小的路径的长度,注意路 ...

  6. bzoj 3747: [POI2015]Kinoman

    (颓废扒题解2333) 给颜色的下一个出现位置记录一下,然后每次只有第一个颜色的出现位置和下一个出现位置之间会产生这种颜色的价值,所以用线段树维护一下区间. 那么现在就只需要把整个的数列从1-> ...

  7. BZOJ #3746: [POI2015]Czarnoksiężnicy okrągłego stołu 动态规划

    转载请注明出处:http://www.cnblogs.com/TSHugh/p/8823423.html 读完题就会发现p=0.1的情况以及n=1.2的情况都可以直接判掉,而p=2的时候也可以直接构造 ...

  8. [最新]免费伪原创SEO接口推荐

    文章目录[隐藏] 前言 接口地址 接口参数说明 前言 其实市面上有很多的伪原创接口,但是许多接口都是收费的,最近在搜索时找到一个免费的接口感觉挺不错的推荐给大家. 接口地址 http://seowyc ...

  9. 【BZOJ 3747】 3747: [POI2015]Kinoman (线段树)

    3747: [POI2015]Kinoman Time Limit: 60 Sec  Memory Limit: 128 MB Submit: 830  Solved: 338 Description ...

最新文章

  1. sql 数字转换为16进制数函数
  2. 养成这8个好习惯 开车会很安全的
  3. php 类加载,关于PHP中类的加载
  4. 5款替代微软Visio的开源免费软件(转)
  5. php yii2 路径问题,yii2常用路径获取
  6. mysql排序区分大小写吗_MySQL的order by时区分大小写
  7. Python默认参数的坑
  8. 开发流程与管理--华为硬件开发
  9. 最新信息安全毕业设计题目选题推荐
  10. 推理和论证(证明)的区别
  11. 三栏式布局的几种实现方式
  12. 香农采样定理(奈奎斯特采样定理)
  13. wps怎么画网络图_wps 流程图怎么画 WPS流程图绘制图解教程
  14. 13. 中国古代数学家张丘建在他的《算经》中提出了一个著名的“百钱百鸡问题”:一只公鸡值5钱,一只母鸡值3钱,三只小鸡值1钱,现在要用百钱买百鸡,请问公鸡、母鸡、小鸡各多少只?
  15. 一文读懂什么是自由城Free City
  16. html页面退出关闭定时器,关闭settimeout setTimeout函数问题
  17. 如何使用ssh连接windows?
  18. FFMpeg ver 20160219-git-98a0053 滤镜中英文对照 2016.02.21 by 1CM
  19. 怎样将语音转化为文字
  20. 从头到脚说单测——谈有效的单元测试(上篇)

热门文章

  1. 查找数据挖掘的相关资料
  2. Chrome 技巧篇-浏览器网页设置编码,解决网页乱码问题,最新版charset插件获取,UTF-8编码设置
  3. 基于STM32分析栈、堆、全局区、常量区、代码区、RAM、ROM
  4. 字符串(0-9和小数点)转为数字--atof,数字转化为字符串--sprintf
  5. CTFshow php特性 web109
  6. 牛顿迭代法(Newton's Method)
  7. CodeForces - 831D Office Keys
  8. C++中的vector使用范例-
  9. flask-bootstrap-高亮-下划线-删除线-加粗-斜体
  10. java如何实现redis分片存储_面试官:你说一下Redis吧,怎么实现高可用,还有持久化怎么做的?...