Leetcode复盘5——字符串

Leetcode复盘5——字符串

导读
1.有效的字母异位词 / 两个字符串包含的字符是否完全相同(Leetcode242)

难度:简单Easy

idea: 哈希表(HashMap) / 字典(dict)
用字典来存储每个单词出现的次数,最后看两个字符串相同字母出现的次数是否一样,或者因为只有26个字母,故可以用一个长度为26的字符串来记录每个字母出现的次数,分别扫描两个字符串,一个用来加,另一个用来减,最后再检查一遍是否每个字母出现次数都为0了

代码:
Java版

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] cnts = new int[26];                                   // 左边一定要加[],即int[],不然是把int[26]这个数组赋给整数型int了
        for(char c: s.toCharArray()) {                               // 把字符串s转换成字符串数组,然后用for循环去取
            cnts[c - 'a']++;                                         // 计算字符c和字母a之间的差距,然后在cnts对应位置+1
        }
        for(char c: t.toCharArray()) {
            cnts[c - 'a']--;
        }
        for(int cnt : cnts) {                                         // cnts里面存的是每个字母出现的次数
            if(cnt != 0) {
                return false;
            }
        }
        return true;
    }
}
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        dic = {}                                            # 把鼠标放到dic={}上,会显示 (variable) dic: dict[Any, Any]
        # 1.加
        for char in s:                                      # char在Python中不是关键字,在Java中是
            if char not in dic:
                dic[char] = 1
            else:
                dic[char] += 1
        # 2.减
        for char in t:
            if char not in dic:
                return False
            else:
                dic[char] -= 1
        # 3.检查是否为0
        for cnt in dic.values():                            # 遍历每个key的value
            if cnt != 0:
                return False
        
        return True
2.最长回文串 / 计算一组字符集合可以组成的回文字符串的最大长度(Leetcode409)

难度:简单Easy

idea: 哈希表(HashMap)
利用类似于哈希表的方法,建立一个长度为58的数组,用来存储各个字母出现的次数(哈希表也能存次数,但是此题数组就能解决了),因为A的ACSII码为65,z的ACSII码为122,还是用[c - ‘A’]这个老方法统计完次数后开始用for循环看每一个次数,当次数为偶数时,不管为多少次都可以直接用,但当次数为奇数时,需要减掉1次,因为多出来一个;但是有一个奇数字母例外,它可以把多出来的那个字母放在中间,只有一个奇数字母可以这么搞.最后总次数+1

代码
C++版

class Solution {
public:
    int longestPalindrome(string s) {
        int cnts[52] = {0};
        for(string::size_type i = 0; i < s.size(); i++) {
            if('a' <= s[i] && s[i] <= 'z') {                       // C++就可以用s[i]直接取,不用转换
                cnts[s[i] - 'a']++;
            } else {
                cnts[s[i] - 'A' + 26]++;                  // 大写字母要放到大写的对应次数那里,故要+26
            }
        }
        int res = 0;                                        // 用来记录字母总和
        for(int i = 0; i < 52; i++) {
            // 知识点: 当x为偶数(对应二进制末位为0)时,x&1=0; 当x为奇数时(对应二进制末位为1),x&1=1;
            if((res & 1) && (cnts[i] & 1)) {            // 当且仅当res已经为奇数了(证明已经有一个放在正中间了)且当前字母cnts[i]还是为奇数,则需要-1再放到最终结果里,因为至多只能有一个奇数字母.
                res += cnts[i] - 1;
            } else {
                res += cnts[i];
            }
        }
        return res;
    }
};

Java版

class Solution {
    public int longestPalindrome(String s) {
        int[] cnts = new int[58];                   // 因为区分大小写,A的ACSII码为65,z的ACSII码为122
        for(char c : s.toCharArray()) {             // Java麻烦就麻烦在没办法用for直接取字符串,须转换
            cnts[c - 'A']++;
        }

        int res = 0;
        for(int cnt : cnts) {
            // 看一下每个字符出现的次数,最多用偶数次,比如2,4,6,8...都可以用,奇数次要-1,因为多了一个,例如3,5,7...但有一种特殊情况,当有好多个字母出现奇数次时,其中有一个是可以用的,把它放到最中间.
            res += cnt - (cnt & 1);                 // 当cnt为偶数时,cnt&1=0; 当cnt为奇数时,cnt&1=1
        }
        // 如果最终的长度小于原字符串的长度,说明里面至少有一个字符出现了奇数次,那么那个字符可以放在回文串的中间,所以额外再加一
        return res < s.length() ? res + 1 : res;
    }
}
3.同构字符串 / 字符串同构(LeetCode205)

难度:简单Easy

idea: 哈希表(HashMap)
利用一个 map 来处理映射,对于s到t的映射,我们同时遍历s和t,假设当前遇到的字母分别是c1和c2,分两步:
如果map[c1]不存在,那么就将c1映射到c2,即map[c1]=c2;
如果map[c1]存在,那么就判断map[c1]是否等于c2,也就是验证之前的映射和当前的字母是否相同.
需要遍历两边,即s->t和t->s都需要遍历,
e.g:
s = ab, t = cc
第一遍遍历: a->c, b->c没问题;
第二遍遍历: c->a, c->b就有问题了;

代码:
Java版

class Solution {
    public boolean isIsomorphic(String s, String t) {
        return isIsomorphicHelper(s, t) && isIsomorphicHelper(t, s);    // 须遍历两遍,故定义一个helper函数
    }

    private boolean isIsomorphicHelper(String s, String t) {
        int n = s.length();
        HashMap<Character, Character> map = new HashMap<>();            // Java定义就是麻烦,每次都new
        for (int i = 0; i < n; i++) {
            char c1 = s.charAt(i);                                      // 字符串取字母,用charAt()
            char c2 = t.charAt(i);
            if (map.containsKey(c1)) {                                  // 当map中已经存在该key了
                if (map.get(c1) != c2) {                                // 若get取到的value和当前不一致
                    return false;
                }
            } else {
                map.put(c1, c2);
            }
        }
        return true;
    }
}

idea: 哈希表(HashMap) / 字典(dictionary)
跟Java法不同,这次不扫描两遍了,把每组映射都转换成数字,例如 a->1, b->1, g->2, f->2,对于当前的两个字母,看它们之前是否出现过,若一个出现过,一个没出现则肯定不是同构,
e.g
s = ab, t = cc
第一轮: a->1, c->1;
第二轮此时: b -> 0(初始), c -> 1,就不同了,返回false

Python版

from collections import defaultdict

class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        counter_1 = defaultdict(int)
        counter_2 = defaultdict(int)
        for i in range(len(s)):
            c1,c2 = s[i], t[i]
            if counter_1[c1] != counter_2[c2]:                  # 若其中至少有一个出现过了,值肯定不是0了
                return False
            else:
                # 是否已经建立过一对一映射,建立过就不需要再处理;
                if counter_1[c1] == 0:                          # 其实这句话也可以不加,大不了两个已经存在映射value值都+1呗
                    counter_1[c1] = i + 1
                    counter_2[c2] = i + 1
        return True
4.回文子串 / 回文子字符串个数(LeetCode647)

难度:简单Easy

idea: 中心扩展法(extendPalindrome)
以center为中心左右扩展,一共有2length-1个center,分别是length个单字符center(例如a)和length-1个双字符center(例如ab)

代码:
C++版

class Solution {
public:
    int countSubstrings(string s) {
        int n = s.size();
        int count = 0;                                    // 虽然count是函数内定义的,但是也把它放到palindromic函数里(记得加取地址符号&来引用,不然就是复制了),所以count是跟着变化的
        for(int i = 0; i < n; i++) {
            palindromic(s, i, i, count);                  // 1个中心点,奇数子串
            palindromic(s, i, i + 1, count);              // 2个中心点,偶数子串
        }
        return count;
    }
    
private:
    void palindromic(string s, int left, int right, int& count) {      // 看以i为中心点的回文子串有几个
        while(left >=0 && right < s.size() && s[left] == s[right]) {
            count++;
            left--;
            right++;
        }
    }
};

Java版

class Solution {
    int count = 0;                                          // 全局变量count,在所有函数外定义
    
    public int countSubstrings(String s) {
        if(s == null || s.length() == 0) return 0;
        
        for(int i = 0; i < s.length(); i++) {              // i为中心点,即center
            extendPalindrome(s, i, i);                      // 中心点为1个字母
            extendPalindrome(s, i, i + 1);                  // 中心点为2个字母
        }
        return count;
    }
    
    private void extendPalindrome(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {  // 字符串型取长度length要加括号
            count++; 
            left--; 
            right++;
        }
    }
}
5.回文数 / 判断一个整数是否是回文数(LeetCode9)

idea: 数学解法(math)
通过取整(取头部的数字)和取余(取尾部的数字)操作获取整数中对应的数字进行比较
e.g num = “1221”
1221 / 1000得首位1, 1221 % 10得末位1;
1221 / 100得百位2, 1221 % 100得十位2;

代码
Java版

class Solution {
    public boolean isPalindrome(int x) {
        // 边界判断
        if(x < 0) 
            return false;
        // 先求最高位,如果x除以div大于10的话,div再翻10倍,直到x/div为个位,此时即最高位
        int div = 1;
        while(x / div >= 10) {
            div *= 10;
        }
        // 此时div已经可以得到x的最高位了,开始比较
        while(x > 0) {
            int left = x / div;
            int right = x % 10;
            if(left != right) return false;
            // x也需要变化,由1221变成22:
            x = (x % div) / 10;                             // %div的目的是得到除最高位以外的数,/10的目的是去掉最低位;
            div /= 100;
        }
        return true;
    }
}
6.计数二进制子串 / 循环数组中比当前元素大的下一个元素(LeetCode696)

idea: 字符串
每当遇到一个新的数字时(新的字符c不等于最后一个字符last了),
之前数字pre和新数字之前的cur都定下来了,统计二者的较小值,再把cur数字个数赋给pre,因为接下来cur换成新数字了,故pre也变成上一个数字了.cur从1开始记(即记新的数字个数了)
e.g: 00011101
第一轮: c=0,last!=c,last=0,ans=0.cnt_pre=0,cnt_cur=1;
第二轮: c=0,lastc,cnt_cur=2;
第三轮: c=0,last
c,cnt_cur=3;
第四轮: c=1,last!=c,last=1,ans=0(之前有0个空,当前有3个0).cnt_pre=3(即0的个数记为3),cnt_cur=1;
第五轮: c=1,last!=c,cnt_cur=2;
第六轮: c=1,last!=c,cnt_cur=3;
第七轮: c=0,last!=c,last=0,ans=3(之前有3个0,当前有3个1).cnt_pre=3(即1的个数记为3),cnt_cur=1;
第八轮: c=1,last!=c,last=1,ans=3+1=4(之前有3个1,当前有1个3).cnt_pre=1(即0的个数记为1),cnt_cur=1;
第九轮: c=-,last!=c,last=-,ans=4+1=5(之前有1个0,当前有1个1).cnt_pre=1(即1的个数记为1),cnt_cur=1;

代码:
C++版

class Solution {
public:
    int countBinarySubstrings(string s) {
        int res = 0;
        char last = '-';
        int cnt_pre = 0;
        int cnt_cur = 0;

        s += '-';
        for (auto c : s) {
            if (last != c) {
                last = c;
                res += min(cnt_pre, cnt_cur);            // 注意可能会出现多个01,但是位置不同,也算不同个
                cnt_pre = cnt_cur;
                cnt_cur = 0;
            }
            cnt_cur++;
        }
        return res;
    }
};

idea: 字符串
思路跟上一个类似,下面是具体执行过程
e.g: 00011101
第一轮: n = 0, pre = 0, curr = 1, s[0] = s[1], curr = 2;
第二轮: n = 0, pre = 0, curr = 2, s[1] = s[2], curr = 3;
第三轮: n = 0, pre = 0, curr = 3, s[2] != s3, pre = 3(此时指3个0); curr = 1, (pre >= curr), n = 1(3个0,1个1,可以凑1个了)
第四轮: n = 1, pre = 3, curr = 1, s[3] = s[4], curr = 2, (pre >= curr), n = 2(3个0,2个1,可以凑2个了)
第五轮: n = 2, pre = 3, curr = 2, s[4] = s[5], curr = 3; (pre >= curr), n = 3(3个0,3个1,可以凑3个了)
第六轮: n = 3, pre = 3, curr = 3, s[5] != s6, pre = 3(此时指3个1), curr = 1, (pre >= curr),n = 4(3个1,1个0,可以凑1个了)
第七轮: n = 4, pre = 3, curr = 1, s[6] != s7, pre = 1(此时指1个0), curr = 1, (pre >= curr),n = 5(1个0,1个1,可以凑1个了)

C++版

class Solution {
public:
    int countBinarySubstrings(string s){                          // s是字符串数组
        int n = 0, pre = 0, curr = 1;
        for (int i = 0; i < s.size() - 1; ++i) {                  // 比到倒数第二个,下标[s.size()-2]
            if(s[i] == s[i + 1]) {                                // 若还跟上一个一样,curr就接着记
                curr++;
            } else {                                            // 若不一样了,把curr给上一个,curr重新计
                pre = curr; 
                curr = 1;
            }
            // 另起一个if语句
            if(pre >= curr) {                        // 只要pre大于等于当前的计数器curr,就能多凑一个出来
                n++;
            }
        }
        return n;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值