仅执行一次字符串交换能否使两个字符串相等&&同构字符串

68 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

[1790. 仅执行一次字符串交换能否使两个字符串相等]

leetcode.cn/problems/ch…

image-20221011203535018

做法:计数统计

题目要求其中一个字符串执行最多一次字符交换使得两个字符串相等,意味着两个字符串中最多只存在两个位置的字符不相等,此时我们只需要交换这两个位置的字符即可,所以我们开辟一个临时数组,存放s1和s2字符不相同位置的下标

  • 如果s1和s2字符不相同位置的下标大于3个 -> 说明不能通过只交换1次使两个字符串相等
  • 如果s1和s2字符不相同位置的下标只有1个 -> 说明二者有一个字符不相同,此时是不能通过只交换1次使两个字符串相等

然后交换这两个位置的下标对应的字符,然后判断两个字符串是否相同

class Solution {
public:
    bool areAlmostEqual(string s1, string s2) {
        vector<int> tmp;//存放s1和s2字符不相同位置的下标
        //s1的长度和s2的长度是一样的
        for(int i = 0;i<s1.size();i++)
        {
            if(s1[i] != s2[i])
            {
                tmp.push_back(i);
            }
        }
        if(tmp.size() == 0) //说明s1和s2的对应位置字符是一样的
            return true;
            
        if(tmp.size() >= 3|| tmp.size() == 1)
            return false;
​
        swap(s1[tmp[0]],s1[tmp[1]]);
        return s1 == s2;
    }
};

[205. 同构字符串]

leetcode.cn/problems/is…

image-20221011203611492

两个字符串同构的含义就是字符串 s可以唯一的映射到t,同时t也可以唯一的映射到s

例如:

  • egg 和 add 同构

就意味着下边的映射成立 e -> a g -> d 也就是将 egg 的 e 换成 a, g 换成 d, 就变成了 add,同时倒过来也成立a -> e d -> g,也就是将 add 的 a 换成 e, d 换成 g, 就变成了 egg

  • foo 和 bar 不同构,原因就是映射不唯一, o -> a, o -> r, 其中 o 映射到了两个字母

我们可以利用一个 map 来处理映射,对于s到t的映射:我们同时遍历字符串s和t,假设当前的字符为:c1和c2

  • 如果map[c1]不存在,那么此时c1字符映射的就是c2字符,即:map[c1] = c2
  • 如果map[c1]已经存在了,那么就判断c1映射的字符map[c1] 是否和c2一样,如果一样,那就说明一个字符映射到了两个字符 ! 返回false

我们不仅要验证s到t方向的映射是正确的,也要保证t到s方向的映射是正确的,因为如果只验证单方向的,可能会出错!例如:s="ab",t="cc",从s到t方向: a映射c,b映射c,没有问题,但是t到方向, c同时映射a和b,一个字母对应了多个字母,所以不算同构的

class Solution {
public:
    //因为不对原字符串做修改,所以传引用
    bool isIsomorphicHelper(string& s,string t)
    {
        unordered_map<char,char> um;
        for(int i = 0;i<s.size();i++)
        {
            char c1 = s[i];
            char c2 = t[i];
            if(um.find(c1) == um.end()) //c1没有映射的字符
            {
                um[c1] = c2;//此时c1映射的字符就是c2
            }
            if(um[c1] != c2)
            {
                return false;
            }
        }
        return true;
    }
    bool isIsomorphic(string s, string t) {
        return isIsomorphicHelper(s,t)&&isIsomorphicHelper(t,s);
    }
};

简化:

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        unordered_map<char, char> map1;
        unordered_map<char, char> map2;
        for (int i = 0, j = 0; i < s.size(); i++, j++) {
            if (map1.find(s[i]) == map1.end()) { // map1保存s[i] 到 t[j]的映射
                map1[s[i]] = t[j];
            }
            if (map2.find(t[j]) == map2.end()) { // map2保存t[j] 到 s[i]的映射
                map2[t[j]] = s[i];
            }
            // 发现映射 对应不上,立刻返回false
            if (map1[s[i]] != t[j] || map2[t[j]] != s[i]) {
                return false;
            }
        }
        return true;
    }
};

数学中映射的相关概念定义:

  • 单射:对于任意 x ,都有唯一的 y 与之对应
  • 满射:对于任意 y ,至少存在一个 x 与之对应
  • 双射:既是单射又是满射,又称为一一对应

image-20221011211211307


神奇做法:

1.因为字符串s和t的长度是一样的,所以无需考虑长度

2.相同的字符用find找到的下标是一样的

例如:有s="foo"和t="baa",当我们到最后一个index时s里找'o't里找'a'一定会返回相同的index,因为之前已经出现过了,这里s里的'o'跟t里的'a'都会返回index 1

例如:有s="foo"和t="bar",'fo'和'ba'是没有问题的,但是当我们到最后一个index时s里的'o'会返回1,因为之前已经出现过,而t里的'r'会返回2,因为之前并没有出现过这个字母,这样就可以验证他们的pattern是否相同

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        for(int i = 0; i < s.size(); ++i)
        {
            if(s.find(s[i]) != t.find(t[i]))
                return false;
        }
        return true;
    }
};