/**
     * leetcode1371 每个元音字母包含偶数次的最长子字符串
     * 方法四 ,前缀和+状态压缩
     * @param s
     * @return
     */
    public static int findTheLongestSubstring(String s){
        int n = s.length();
        int[] pos = new int[1<<5]; // 5个字符,00000~11111,总共有32种状态, 00001表示a出现次数为奇数次,其他字符出现次数为偶数次
        Arrays.fill(pos,-1);
        int ans = 0,status = 0;
        pos[0] = 0;
        for(int i=0;i<n;i++){
            char ch = s.charAt(i);
            if(ch == 'a'){
                status ^= (1<<0); //00001 ,^是异或运算符,<<是左移运算符 初始如果从来没出现过是这样的 00000<<00001=00001 ,但是如果e出现过那么 00010 << 00001 就变成 00011
            }else if(ch == 'e'){
                status ^= (1<<1); //00010
            }else if(ch == 'i'){
                status ^= (1<<2); //00100
            }else if(ch == 'o'){
                status ^= (1<<3); //01000
            }else if(ch == 'u'){
                status ^= (1<<4); //10000
            }
            if(pos[status] >= 0){
                if(ans < i+1-pos[status]){
                    System.out.println(String.format("目前为止符合条件的最长子字符串为%s",s.substring(pos[status],i+1)));
                }
                ans = Math.max(ans,i+1-pos[status]);
            }else {
                //如果该奇偶状态位置从未记录过,记录第一次出现该奇偶状态所在的位置
                pos[status]=i+1;
            }
        }
        return ans;
    }


    /**
     * 遍历子字符串
     * @param s
     * @return
     */
    static List<String> test003(String s){
        List<String> list = new ArrayList<>();
        for (int i = 0; i <s.length() ; i++) {
            for (int j = i+1; j <=s.length() ; j++) {
                list.add(s.substring(i,j));
                System.out.println(s.substring(i,j));
            }
        }
        return list;
    }

    @Data
    static class Result1 {
        private int maxSubLength;
        private String maxSubString;
        private int start;
        private int end;
        List<Result1> subStringList;
    }


    /**
     * 求1和2都出现偶数次的最长子字符串长度
     * 输入示例 2231145
     * 输出 7
     * 说明:最长子字符串是  2231145, 它刚好包含两个1和两个2
     * 方法一 暴力穷举,遍历字符串的所有子串
     * @param s
     * @return
     */
    static Result1 findTheLongestSubstring1(String s){
        Result1 result1 = new Result1();
        char[] chars = s.toCharArray();
        int len = s.length();
        int maxSubLength = 0;
        int start=0;
        int end = 0;
        List<Result1> subStringList = new ArrayList<>();
        String maxSubString = "";
        for (int i = 0; i <len ; i++) {
            for (int j = i+1; j <=len ; j++) {
                int cnt1 = 0;
                int cnt2 = 0;
                for (int k = i; k <j ; k++) {
                    if(chars[k]=='1'){
                        cnt1++;
                    }else if(chars[k]=='2') {
                        cnt2++;
                    }
                }
                if(cnt1%2==0 && cnt2%2==0){
                    String substring = String.valueOf(chars,i,j-i);
                    System.out.println(substring);
                    if( (j-i) > maxSubLength){
                        maxSubLength = j-i;
                        maxSubString = String.valueOf(chars,i,j-i);
                        start = i;
                        end = j;
                    }
                    Result1 result11 = new Result1();
                    result11.setMaxSubLength(j-i);
                    result11.setMaxSubString(substring);
                    result11.setStart(i);
                    result11.setEnd(j);
                    subStringList.add(result11);
                }
            }
        }
        System.out.println(maxSubLength);
        System.out.println(maxSubString);
        result1.setMaxSubLength(maxSubLength);
        result1.setMaxSubString(maxSubString);
        result1.setStart(start);
        result1.setEnd(end);
        result1.setSubStringList(subStringList);
        return result1;
    }

    /**
     * 方法二 利用前缀和数组
     * 定义pre[i][k]表示在字符串前i个字符中,第k个字符一共出现的次数,
     * 假设我们需要求出[l,r]这个区间的子串某个字符出现的次数,就可以用pre[r][k]-pre[l-1][k]
     * 利用前缀和优化了统计子串的时间复杂度,然而枚举所有的子串复杂度仍需要O(n^2)
     */
    static void findTheLongestSubstring2(){
        String s = "122231145";
        int[][] pre = new int[s.length()+1][2];
        char[] chars = s.toCharArray();
        int len = s.length();
        int maxSubLength = 0;
        String maxSubString = "";
        int t1=0;
        int t2=0;
        for (int i = 1; i <=len ; i++) {
            if(chars[i-1]=='1'){
                pre[i][0] = ++t1;
            }else {
                pre[i][0] = t1;
            }
            if(chars[i-1]=='2'){
                pre[i][1] = ++t2;
            }else {
                pre[i][1] = t2;
            }
        }
        for (int i = 1; i <=len ; i++) {
            for (int j = i; j <=len ; j++) {
                int cnt1 = pre[j][0] - pre[i-1][0];
                int cnt2 = pre[j][1] - pre[i-1][1];
                System.out.println(String.format("区间%d~%d,1出现次数%d",i,j,cnt1));
                System.out.println(String.format("区间%d~%d,2出现次数%d",i,j,cnt2));
                if(cnt1%2==0 && cnt2%2==0){
                    if( j-(i-1) > maxSubLength){
                        maxSubLength = j-(i-1);
                        maxSubString = String.valueOf(chars,i-1,j-(i-1));
                    }
                }
            }
        }
        System.out.println(maxSubLength);
        System.out.println(maxSubString);
    }

    /**
     * 方法三,在获取前缀和的同时就去看遍历到当前字符位置i,从第1个位置找到i,看满足条件的区间j~i
     * 利用前缀和优化了统计子串的时间复杂度,然而枚举所有的子串复杂度仍需要O(n^2),
     * 可以这样优化,枚举字符串每个位置i,计算以它结尾的满足条件的最长字符串的长度。找到最小的j满足
     * pre[i][k]-pre[j][k]最小的j,长度就是i-j
     * @param
     */
    static void findTheLongestSubstring3(){
        String s = "122231145";
        int[][] pre = new int[s.length()+1][2];
        char[] chars = s.toCharArray();
        int len = s.length();
        int maxSubLength = 0;
        String maxSubString = "";
        int t1=0;
        int t2=0;
        for (int i = 1; i <=len ; i++) {
            if(chars[i-1]=='1'){
                pre[i][0] = ++t1;
            }else {
                pre[i][0] = t1;
            }
            if(chars[i-1]=='2'){
                pre[i][1] = ++t2;
            }else {
                pre[i][1] = t2;
            }
            for (int j = 1; j <=i ; j++) {
                int cnt1 = pre[i][0] - pre[j-1][0];
                int cnt2 = pre[i][1] - pre[j-1][1];
                System.out.println(String.format("区间%d~%d,1出现次数%d",j,i,cnt1));
                System.out.println(String.format("区间%d~%d,2出现次数%d",j,i,cnt2));
                if(cnt1%2==0 && cnt2%2==0){
                    if( i-(j-1) > maxSubLength){
                        maxSubLength = i-(j-1);
                        maxSubString = String.valueOf(chars,j-1,i-(j-1));
                    }
                }
            }
        }
        System.out.println(maxSubLength);
        System.out.println(maxSubString);
    }

    /**
     * 方法四,利用奇偶性
     * 偶数这个条件其实告诉了我们,对于满足条件的子串而言,两个前缀和 pre[i][k]\textit{pre}[i][k]pre[i][k] 和 pre[j][k]\textit{pre}[j][k]pre[j][k] 的奇偶性一定是相同的
     * 此时我们就可以利用哈希表存储每一种奇偶性(即考虑所有的元音字母)对应最早出现的位置,边遍历边更新答案。
     */
    static void findTheLongestSubstring4(){
        String s = "122231145";
        char[] chars = s.toCharArray();
        int len = s.length();
        int maxSubLength = 0;
        int status00 = -1;
        int status01 = -1;
        int status10 = -1;
        int status11 = -1;
        int status1 = 0;
        int status2 = 0;
        for (int i = 0; i <len ; i++) {
           if(chars[i]=='1'){
               status1 ^= 1;
           }else if(chars[i]=='2'){
               status2 ^= 1;
           }
           if(status1==0 && status2==0){
               if(status00==-1){
                   status00 = i;
               }
               maxSubLength = Math.max(maxSubLength,i-status00);
           }else if(status1==0 && status2==1){
               if(status01==-1){
                   status01 = i;
               }
               maxSubLength = Math.max(maxSubLength,i-status01);
           }else if(status1==1 && status2==0){
               if(status10==-1){
                   status10 = i;
               }
               maxSubLength = Math.max(maxSubLength,i-status10);
           }else if(status1==1 && status2==1){
               if(status11==-1){
                   status11 = i;
               }
               maxSubLength = Math.max(maxSubLength,i-status11);
           }

        }
        System.out.println(maxSubLength);
    }



    static int findTheLongestSubstring6(String s){
        int[][] pre = new int[s.length()+1][5];
        char[] chars = s.toCharArray();
        int len = s.length();
        int maxSubLength = 0;
        String maxSubString = "";
        int t1=0;
        int t2=0;
        int t3=0;
        int t4=0;
        int t5=0;
        for (int i = 1; i <=len ; i++) {
            if(chars[i-1]=='a'){
                pre[i][0] = ++t1;
            }else {
                pre[i][0] = t1;
            }
            if(chars[i-1]=='e'){
                pre[i][1] = ++t2;
            }else {
                pre[i][1] = t2;
            }

            if(chars[i-1]=='i'){
                pre[i][2] = ++t3;
            }else {
                pre[i][2] = t3;
            }
            if(chars[i-1]=='o'){
                pre[i][3] = ++t4;
            }else {
                pre[i][3] = t4;
            }

            if(chars[i-1]=='u'){
                pre[i][4] = ++t5;
            }else {
                pre[i][4] = t5;
            }

            for (int j = 1; j <=i ; j++) {
                int cnt1 = pre[i][0] - pre[j-1][0];
                int cnt2 = pre[i][1] - pre[j-1][1];
                int cnt3 = pre[i][2] - pre[j-1][2];
                int cnt4 = pre[i][3] - pre[j-1][3];
                int cnt5 = pre[i][4] - pre[j-1][4];
                if(cnt1%2==0 && cnt2%2==0 && cnt3%2==0 && cnt4%2==0 && cnt5%2==0){
                    if( i-(j-1) > maxSubLength){
                        maxSubLength = i-(j-1);
                        maxSubString = String.valueOf(chars,j-1,i-(j-1));
                    }
                }
            }
        }
        return maxSubLength;
    }