zoukankan      html  css  js  c++  java
  • leetcode 1371 包含偶数个元音字母的子字符串

    给定一个字符串,寻找一个最长子字符串,使得包含的原因字母都是偶数个。

    我们很容易想到,暴力枚举。然后通过一点点巧妙的减支,可以通过。

    首先扫一遍字符串,统计每个字符出现的次数。实际上我们只关注元音字母,不过直接全部统计比较方便(我估计时间上也会快一点)

    然后我们枚举 i ~ j ,i 从0 到 len , j 从 len到 i。然后判断 i j组成的子字符串是否符合要求。

    注意两个剪枝条件: 如果 i - len 的长度小于已知的最大值,则 结束。

    如果 i - j 的距离小于已知的最大值,则结束。

    public static int findTheLongestSubstring(String s) {
            int maxLength = -1;
            int[] count = new int[26];
            int len = s.length();
            for(int i=0;i<len;i++){
                count[s.charAt(i)-'a']++;
            }
            char[] Vowels = new char[]{'a','e','i','o','u'};
            int vowelsEvenNum = 0;
            for(int i=0;i<5;i++){
                if(count[Vowels[i]-'a']%2==0) vowelsEvenNum++;
            }
            if(vowelsEvenNum==5) return len;
            for(int i=0;i<len;i++){
                if(len-i<maxLength) break;
                if(i>0) count[s.charAt(i-1)-'a']--;
                int[] tmp = count.clone();
                for(int j=len-1;j>=i;j--){
                    if(maxLength>j-i+1) break;
                    if(j<len-1) tmp[s.charAt(j+1)-'a']--;
                    vowelsEvenNum=0;
                    for(int k=0;k<5;k++){
                        if(tmp[Vowels[k]-'a']%2==0) vowelsEvenNum++;
                    }
                    if(vowelsEvenNum==5){
                        maxLength=Math.max(maxLength,j-i+1);
                    }
    
    
                }
            }
            return maxLength;
    View Code

    我们优化一下。首先想到前缀和。 我们用 pre[i][k]表示第i个字符前,字符k有多少个。

    则我们的子字符串满足:对于任意的k pre[i][k] - pre[j][k] 是偶数。同样的枚举 i , j 。 我估计上面的剪枝同样有效。

    但是有新的优化,我们小学数学学过:

    奇数-奇数=偶数

    偶数-偶数=偶数

    所以我们其实不用记录每个字符出现的个数,只需记录每个字符的奇偶性就可以了。这样的话,我们设计一个特殊的key:

    { a:,e: ,i: ,o:, u:} *:表示*字符的奇偶性。我们利用这样的特殊结构当做key,存储它第一次出现的位置。我们就可以利用哈希表通过一次遍历解决。

    大概代码是这样的:

            Map<STA, Integer> poss = new HashMap<>();
    
            for (int i = 0; i < s.length(); i++) {
                STA sta = new STA();
                if (s.charAt(i) == 'a') sta.a ^= 1;
                if (s.charAt(i) == 'e') sta.e ^= 1;
                if (s.charAt(i) == 'i') sta.i ^= 1;
                if (s.charAt(i) == 'o') sta.o ^= 1;
                if (s.charAt(i) == 'u') sta.u ^= 1;
                if (poss.containsKey(sta)) {
                    ans = Math.max(ans, i - poss.get(sta) + 1);
                } else {
                    poss.put(sta, i);
                }    

    这里我们要新加一个STA的类,而且要重写equal和hashCode方法。不太友好。其实我们只是用到了5个字母。我们用二进制来表示这个5个字母的奇偶性。1表示奇数个,0表示偶数个。我们只需要一个1<<5长度的数组就可以罗列所有状态(出现了出现了,有一个装逼利器:状态压缩)

    这里有个编程的技巧。我们把00000,即所有字母都是偶数个的状态的位置设为0. 其他状态的位置设置为 i+1 .。这是符合逻辑的,因为一个都不选,长度为0,所有的元音字母的个数都为偶数。

    然后这样的话,当一个状态出现两次的时候,它的长度就是 i+1 - pos[status],这样就不用考虑边界值的问题了。

    public static int findTheLongestSubstring2(String s) {
            int ans = 0;
            int[] pos = new int[1 << 5];
            int status = 0;
            Arrays.fill(pos, -1);
            char[] sChar = s.toCharArray();
            int len = s.length();
            pos[0] = 0;
            for (int i = 0; i < len; i++) {
                char c = s.charAt(i);
                if (c == 'a') status ^= 1 << 0;
                if (c == 'e') status ^= 1 << 1;
                if (c == 'i') status ^= 1 << 2;
                if (c == 'o') status ^= 1 << 3;
                if (c == 'u') status ^= 1 << 4;
                if (pos[status] >= 0)
                    ans = Math.max(ans, i + 1 - pos[status]);
                else
                    pos[status] = i + 1;
            }
           return ans;
    }
    View Code
  • 相关阅读:
    PHP 方法整合类 -- 1.根据概率产生随机数 --2.判断手机号归属地及运营商 --3.过滤emoji表情
    PHP 多图下载并打包压缩方法
    PHP 导出excel 精简版
    PHP获取首字母相关方法
    no input file specified 解决办法
    百度地图相关
    经纬度相关方法
    阿里云SSL证书部署至宝塔
    微信入口、生成菜单,公众号授权获取用户信息(unionid)
    超级好用超级简单的支付类库
  • 原文地址:https://www.cnblogs.com/superxuezhazha/p/12924117.html
Copyright © 2011-2022 走看看