leetcode1371-每个元音包含偶数次的最长子字符串

原题

给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 'a','e','i','o','u' ,在子字符串中都恰好出现了偶数次。

示例 1:

输入: s = "eleetminicoworoep"
输出: 13
解释: 最长子字符串是 "leetminicowor" ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。

示例 2:

输入: s = "leetcodeisgreat"
输出: 5
解释: 最长子字符串是 "leetc" ,其中包含 2 个 e 。

示例 3:

输入: s = "bcbcbc"
输出: 6
解释: 这个示例中,字符串 "bcbcbc" 本身就是最长的,因为所有的元音 a,e,i,o,u 都出现了 0 次。

提示:

  • 1 <= s.length <= 5 x 10^5
  • s 只包含小写英文字母。

解法

思想

使用官方题解的前缀和+状态压缩!

这道题不好想,如果用暴力在leetcode绝对超时,可以想到用前缀和的方法。

前缀和怎么做呢?如果要两个字符串的差值(字符串交集)中,a,e,i,o,u全部为偶数,那么根据数学知识,偶数减偶数是偶数,奇数减奇数也是偶数。也就是说两个字符串中的这五个字母出现的奇偶性必须一样!

而五个字母,每个字母出现的次数都可奇可偶,组合出来的情况有很多种,在遍历的时候遇到其中一种奇偶性组合,如何知道最早出现的同一种奇偶性组合出现在哪个位置呢?

这里有一种方法:遍历的时候记录并计算所有字母的个数,然后对每次遍历到的位置,将它的奇偶性组合编码成字符串,例如“奇偶奇偶奇”,代表a,i,u出现奇数个,而e,o出现偶数个。然后就以这个字符串为键,第一次出现的顺序为值存放到哈希表中。后面只要出现相同的奇偶性排列,就把它从哈希表中取出来,就知道最早出现的位置,两个位置一减就是字符串的长度。

但是这种方法需要构造字符串,并且需要记录五种字母出现的个数,十分麻烦,那么可以利用“状态压缩”+“位运算”的方法:将这种奇偶性排列编码成一个五位的二进制位串,例如二进制的"10101",代表a,i,u出现奇数个,而e,o出现偶数个。每一个字母都映射到位串的一个位上,而再出现一个字母的时候,就通过异或运算更改对应位上的值。

例如: "10101" :a,i,u出现奇数个,e,o出现偶数个
(再出现一个i)->"10101"^"100" = "10001",即a,u出现奇数个,e,i,o出现偶数个。

代码

class Solution {
    public int findTheLongestSubstring(String s) {
        int n = s.length();
        int[] pos = new int[1 << 5];
        // 如果是-1,说明还没有出现这种奇偶性排列
        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);
            } else if (ch == 'e') {
                status ^= (1 << 1);
            } else if (ch == 'i') {
                status ^= (1 << 2);
            } else if (ch == 'o') {
                status ^= (1 << 3);
            } else if (ch == 'u') {
                status ^= (1 << 4);
            }
            if (pos[status] >= 0) {
                ans = Math.max(ans, i + 1 - pos[status]);
            } else {
                pos[status] = i + 1;
            }
        }
        return ans;
    }
}

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/leetcode1371-%e6%af%8f%e4%b8%aa%e5%85%83%e9%9f%b3%e5%8c%85%e5%90%ab%e5%81%b6%e6%95%b0%e6%ac%a1%e7%9a%84%e6%9c%80%e9%95%bf%e5%ad%90%e5%ad%97%e7%ac%a6%e4%b8%b2/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年5月19日 15:23
下一篇 2020年5月20日

相关推荐

  • leetcode64-最小路径和

    原题 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明: 每次只能向下或者向右移动一步。 示例: 输入: [ [1,3…

    算法 2020年2月24日
    0110
  • leetcode450-删除二叉搜索树中的节点

    原题 给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。 一…

    2020年1月16日
    0100
  • leetcode145-二叉树的后序遍历

    原题 给定一个二叉树,返回它的 后序 遍历。 示例: 输入: [1,null,2,3]  1   \    2 &nbs…

    算法 2020年1月10日
    080
  • leetcode704-二分查找

    原题 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 示…

    算法 2019年12月29日
    080
  • leetcode374-猜数字大小

    原题 我们正在玩一个猜数字游戏。 游戏规则如下:我从 1 到 n 选择一个数字。 你需要猜我选择了哪个数字。每次你猜错了,我会告诉你这个数字是大了还是小了。你调用一个预先定义好的接…

    算法 2019年12月31日
    0380
  • leetcode62-不同路径

    原题 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Fi…

    2020年2月21日
    0380
  • 剑指offer40-最小的k个数

    原题(来源Leetcode) 输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。 示例 1: …

    算法 2020年3月20日
    0390
  • leetcode349-两个数组的交集

    原题 给定两个数组,编写一个函数来计算它们的交集。 输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2] 示例2: 输入: nums1 = [4…

    算法 2019年12月14日
    090
  • leetcode221-最大正方形

    原题 在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。 示例: 输入: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 …

    算法 2020年5月8日
    0110
  • leetcode152-乘积最大子数组

    原题 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 示例 1: 输入: [2,3,-2,4] 输出:…

    算法 2020年5月18日
    0130

发表回复

登录后才能评论