蓝桥杯国赛之切开字符串

题目:切开字符串

Pear有一个字符串,不过他希望把它切成两段。
这是一个长度为N(<=10^5)的字符串。
Pear希望选择一个位置,把字符串不重复不遗漏地切成两段,长度分别是t和N-t(这两段都必须非空)。

Pear用如下方式评估切割的方案:
定义“正回文子串”为:长度为奇数的回文子串。
设切成的两段字符串中,前一段中有A个不相同的正回文子串,后一段中有B个不相同的非正回文子串,则该方案的得分为A*B。

注意,后一段中的B表示的是:“…非正回文…”,而不是: “…正回文…”。
那么所有的切割方案中,A*B的最大值是多少呢?

【输入数据】
输入第一行一个正整数N(<=10^5)
接下来一行一个字符串,长度为N。该字符串仅包含小写英文字母。
【输出数据】
一行一个正整数,表示所求的A*B的最大值。
【样例输入】
10
bbaaabcaba
【样例输出】
38
【数据范围】
对于20%的数据,N<=100
对于40%的数据,N<=1000
对于100%的数据,N<=10^5

资源约定:
峰值内存消耗(含虚拟机) < 512M
CPU消耗 < 2000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

答案:


import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main
{
    static int n;
    static String str = new String();
    static int max = 0;
    static int A, B, C;

    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);
//      输入字符串的长度
        n = in.nextInt();
//      输入字符串
        str = in.next();
//      for循环遍历切割字符串的方案
        for (int t = 1; t < n - 1; t++)
        {
//          str.substring(0, t) 是本次切割方案的前一段
//          str.substring(t, n) 是本次切割方案的后一段
//          System.out.println("A-------" + str.substring(0, t));
            A = panduan(str.substring(0, t));
            B = quan(str.substring(t, n)) - panduan(str.substring(t, n));
            if (max < A * B)
            {
                max = A * B;
            }
        }
        System.out.println(max);
    }

    /**
     * B的要求是找出 不相同的非正回文子串 这里采用曲线救国的方案,求出B的 全部不相同子串 的个数,减去B中 不相同的正回文子串 的个数
     * quan()   求出一个字符串全部的子串个数
     */
    private static int quan(String sub)
    {
        // TODO Auto-generated method stub
        Set set = new HashSet();
//      i++  这里是+1,是因为要切割出所有的子串。
//      第一层for 定切割的长度
        for (int i = 1; i <= sub.length(); i++)
        {
//          第二层for  定切割的起始位置,起始位置+切割的长度得到一个子串
            for (int start = 0; start <= sub.length() - i; start++)//动态的控制for循环的终止条件
            {
                String s = sub.substring(start, start + i);
//              将子串加入set中
                set.add(s);
            }
        }
        return set.size();
    }

    /**
     * 对于一个字符串,查找其中包含的正回文串个数
     * panduan()  
     */
    private static int panduan(String sub)
    {
//      利用HashSet来装所有的正回文子串,保证元素的唯一性
        Set set = new HashSet();
        set.clear();
//      i += 2  是为了切割出长度为奇数的子串
//      第一层for决定本次循环的子串长度
        for (int i = 1; i <= sub.length(); i += 2)
        {
//          第二层for决定切割的起始位置,起始位置配合长度,切出一个子串。
            for (int start = 0; start <= sub.length() - i; start++)//动态的控制for循环的终止条件
            {
                int nu = start + i;
//              System.out.println(start + "--" + nu);
                String s = sub.substring(start, start + i);
//              System.out.println(s);
//              判断当前的子串是否为回文串
                if (huiwen(s) == true)
                {
                    set.add(s);
                }
            }
        }
        return set.size();
    }

    /**
     * 判断回文
     * huiwen()   判断回文串
     */
    private static boolean huiwen(String s)
    {
        for (int i = 0; i < s.length() / 2; i++)
        {
            if (s.charAt(i) == s.charAt(s.length() - 1 - i))
            {
                continue;
            }
            else
            {
                return false;
            }
        }
        return true;
    }
}

心得:

思路:

        有一段字符串,for循环遍历切割方案,求出每一个方案中A*B的值,求出后立马比较max,如果当前方案中的A*B的值大于max,则更新max的值。
在第t个分割方案中,有前后两段字符串。
A:前一段字符串中所有不相同正回文子串的个数。
除了正回文字串,其余的都是非正回文字串。
B:后一段字符串中所有不相同非正回文子串的个数===》后一段字符串中所有不相同的字串个数 - 所有不相同的正回文字串的个数。

定义“正回文子串”为:长度为奇数的回文子串。

所以我们要写三个方法:
一:求出一个字符串中所有不相同子串的个数:要把一个字符串所有的子串都切出来,然后利用HashSet来保证元素的唯一性,返回HashSet的size。
二:切出一个字符串中所有长度为奇数的子串,然后调用方法三判断当前奇数长度的子串是否为回文串。
三:判断当前的子串是否为回文串。返回boolean值。

“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
5/2 是取整的
“hamburger”.substring(4, 8) returns “urge”
“smiles”.substring(1, 5) returns “mile”
System.out.println(“a”.substring(0, 1));
a
求出所有子串的思路:
按长度进行划分,两层for循环,第一层for决定本次循环的字串长度;第二层for决定切割的起始位置,起始位置配合长度,切出一个子串,利用上一层for的循环变量动态地控制当前for的终止条件。长度不够的话,当然切不了了。

你可能感兴趣的:(蓝桥杯,蓝桥杯训练)