题目描述
小蒟蒻最近对字符串的子序列着了迷。一个字符串 s 被称作另一个字符串 S 的子序列,说明从序列 S 去除某些元素但不破坏余下元素的相对位置(在前或在后)可得到序列 s 。
小蒟蒻想到了如下的问题:给出一个由’a’, ’b’, ’c’ 组成的长度为 n 的字符串。
定义一个子序列 T 的价值如下:若子序列 T存在循环节,则其价值为 (lenT)^2 / cT ,其中 lenT 表示 T 的长度,cT 表示 T 的最小循环节长度;
反之若 T 不存在循环节,则其价值为 0。 找出价值最大的子序列所具有的价值。
何谓循环节?若长度为 x 的字符串 A 具有循环节,则代表存在长度为 y 的字符串B,x % y = 0,且A恰好是由 x / y 个 B首尾相连组合而成的。
当 y 最小时,B称为 A 的最小循环节。
这可难倒了小蒟蒻,你能帮帮他吗?
输入格式
输入的第一行包括一个整数 n(1 ≤n≤ 10000),表示字符串的长度。
输入的第二行包括一个长为 n 的字符串,其组成元素只有’a’, ’b’, ’c’。
输出格式
输出包括一行一个元素ans,表示价值最大的子序列所具有的价值。
样例输入
3
abc
样例输出
3
由抽屉原理易知,对于一个只由'a','b','c'组成的字符串,至少有一种字符的数量大于等于 n/3。那么不妨设'a'的数量大于等于 n/3,我们可以考虑去掉所有的'b'和'c',得到一个只由'a'组成的子序列。由公式 (lenT)^2 / cT 知,该子序列的价值大于等于 n^2/9。知道了该子序列的价值有什么用呢?根据题意,我们需要找的是最大价值的子序列,现在已经证明存在一个子序列的价值大于等于 n^2/9,那么目标子序列的价值一定也大于等于 n^/9。
易知,最终得到的子序列的长度 lenT <= n。
综合以上两个结论,我们可以得到 ans = (lenT)^2 / cT >= n^2 / 9 和 lenT <= n,由此便可推导出cT <= 9,易知以上两个式子不可同时取得等号,故cT < 9。
有了cT < 9,这道题就变成了一道简单的dfs。首先我们生成所有长度小于9的简单循环节。(简单循环节即该字符串有且仅有唯一的循环节为其本身)
1 for (int i = 1; i <= min(8, n); i++) { 2 dfs(0, i); 3 } 4 5 void dfs(int step, int len) { 6 if (step == len) { 7 xhj[len] = '\0'; 8 if (issimple(len)) { 9 check(len); 10 } 11 } 12 else { 13 for (int i = 0; i < 3; i++) { 14 xhj[step] = 'a' + i; 15 dfs(step+1, len); 16 } 17 } 18 }
如何判断一个字符串是否是简单循环节呢?(为了方便,下文用len表示字符串长度)如果len=1,那么该字符串一定是简单循环节。如果len是2的倍数,若它不是简单循环节,那它一定是以2为周期的,我们只需将该字符串平分为两部分,检查前半部分与后半部分是否相等即可。如果len是3的倍数,若它不是简单循环节,那它一定是以3为周期的。同理,我们只需将该字符串平分为三部分,检查这三部分是否相等即可。若len = 5或7,若该字符串不是简单循环节,那它一定是由同一字符重复得到的,我们只需检查是否有两个字符不相等即可。
1 bool issimple(int len) { 2 if (len == 1) { 3 return true; 4 } 5 6 if (len % 2 == 0) { 7 for (int i = 0; i < len/2; i++) { 8 if (xhj[i] != xhj[i+len/2]) { 9 return true; 10 } 11 } 12 } 13 14 if (len % 3 == 0) { 15 for (int i = 0; i < len/3; i++) { 16 if (xhj[i] != xhj[i+len/3] || xhj[i] != xhj[i+len/3*2]) { 17 return true; 18 } 19 } 20 } 21 22 23 if (xhj[0] != xhj[1]) { 24 return true; 25 } 26 27 return false; 28 }
如果是简单循环节,我们再与题目所给字符串进行对比,抽取出由生成循环节循环产生的字符串,再计算出价值,输出其中最大的即可。
1 void check(int len) { 2 long long j = 0, counter = 0; 3 for (long long i = 0; i < n; i++) { 4 if (str[i] == xhj[j]) { 5 counter++; 6 j++; 7 if (j >= len) { 8 j = 0; 9 } 10 } 11 } 12 13 counter = counter / len * len; 14 long long temp = counter * counter / len; 15 ans = (ans > temp) ? ans : temp; 16 }
如何找到我们生成的循环节对应的最长子序列呢?通过一一对比,我们很容易可以得到由循环节循环生成的子序列长度couter,但要注意这里的counter并不是真正的子序列长度,因为有可能找到的该段子序列末尾是一段不完整的子序列,所以我们需要通过counter = counter / len * len将其截断,这样就得到了子序列的真正长度。