[字符串 哈希表] 205. 同构字符串(利用哈希表寻找双向映射关系)

10 篇文章 0 订阅
9 篇文章 0 订阅

205.同构字符串

题目链接:https://leetcode-cn.com/problems/isomorphic-strings/

分类

  • 字符串(给定新概念“同构”,总结出具体要求和实现方法,提取出关键词:双向映射);
  • 哈希表(记录两个字符串上字母的映射关系);
  • Set(统计字符串上的字母种类个数)

在这里插入图片描述

题目分析

同构的要求题目已经很明确的给出了:

  1. 所有出现的字符都必须用另一个字符替换
  2. 两个字符不能映射到同一个字符上;
  3. 字符可以映射到本身;
  4. 映射后保留字符的顺序。

使用map就能解决上述的要求,但注意,一个map中key=s上的字母,value=映射到t上的字母,所以一个map只能提供单向的s -> t映射关系,而t -> s的映射关系没有考虑在内,所以这题的难点在于

  1. 是否意识到存在两个方向的映射关系(s->t,t->s);
  2. 如何实现s->t,t->s两个方向的映射
  • 思路1采用的是map+set+字母种类数比较,只用了一个map用来记录s->t的映射关系,set用来统计t的字母种类数,如果同时满足:s->t字母映射关系合法,且s,t字母种类数相等,则s,t同构。
  • 思路2采用的是一个map + 调换参数执行两次函数
  • 思路3采用的是两个数组分别记录s->t,t->s的映射关系

思路1:map + set + 字母种类数比较(自己想到的,推荐)

算法设计

1、要求1可以转化为:s,t中字母的种类和字母数量要对应。

示例1:s = “egg”, t = “add”
s中有两种字母e和g,t中也有两种字母a和d;

s中有一个个数为1的字母,有一个个数为2的字母,相应的t中也有一个个数为1的字母,一个个数为2的字母,所以两个字符串满足要求1。

又因为s,t长度相同,所以只要字母种类相同,它们的数量也就自然能够满足对应关系。

如何统计s,t上字母的种类数量

在后面使用了map以后,map的键值对数量=s的字母种类数,需要再开辟一个set来存放t的无重复字母,最后set的大小 == t的字母种类数。

2、要求2可以用map来实现,key=s上的字母,value=映射到t上的字母
一旦出现同一个key而value不同,说明 t 上的两个字符映射到s上的同一个字符了,不满足要求1,直接返回false;
但是这个map只提供了s -> t的映射,如果出现:s=ab,t=cc,a->c,b->c合法,但t->s时c同时映射了两个字母,所以是不合法的:

  • 解决方法1:map结合算法设计1里的处理,统计字母的种类数量;(思路1)
  • 解决方法2:分别以s,t和t,s作为参数执行两次,就能分别处理s->t和t->s两个映射,最后两个函数的返回值相与的结果就是最终结果。(见思路2)
  • 解决方法3:不用map,设置两个数组提供map的效果,分别存放s->t和t->s的字母映射关系,(见思路3)

3、要求3不需要对映射规则做任何限制,就能满足字符可以映射到本身的要求;

4、要求4只需要做到同时遍历s,t同一个索引位置上的字符就能满足

使用了map后,我们遍历两个字符串都从0开始访问同一个下标的字母,同一个下标处s上的字母和t上的字母两两配对,所以相对顺序在遍历过程中被隐性保存下来了。

实现代码
class Solution {
    public boolean isIsomorphic(String s, String t) {
        Map<Character, Character> map = new HashMap<>();
        Set<Character> set = new HashSet<>();//统计t字符串上的字母种类数
        //s的字母种类数不用统计,就等于map.size()
        for(int i = 0; i < s.length(); i++){
            char sCh = s.charAt(i), tCh = t.charAt(i);
            if(map.containsKey(sCh)){
                if(tCh != map.get(sCh)) return false;
            }
            else{
                map.put(sCh, tCh);
            }
            set.add(tCh);
        }
        return map.size() == set.size();
    }
}

思路2:map + 调换参数执行两次

(参考:https://leetcode-cn.com/problems/isomorphic-strings/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-42/

设置一个check函数用来判断一个方向的映射关系是否合法。

check函数中只使用了一个map,作用和思路1相同,记录单个方向上字母的映射关系。

调用两次check函数,相当于考虑了两个方向上的映射关系:

check(s, t) && check(t, s),

最后,返回两个函数返回值相与的结果即可。

实现代码

class Solution {
    public boolean isIsomorphic(String s, String t) {
        return check(s, t) && check(t, s);
    }
    public boolean check(String s, String t){
        Map<Character, Character> map = new HashMap<>();
        for(int i = 0; i < s.length(); i++){
            char sCh = s.charAt(i), tCh = t.charAt(i);
            if(map.containsKey(sCh)){
                if(tCh != map.get(sCh)) return false;
            }
            else{
                map.put(sCh, tCh);
            }
        }
        return true;
    }
}

思路3:两个数组记录s->t,t->s(较简洁,推荐)

(参考:https://leetcode-cn.com/problems/isomorphic-strings/solution/bu-yong-ha-xi-biao-de-zuo-fa-by-demian-w/

设置两个数组分别记录两个方向的映射关系,数组下标对应字符的ASCII值,元素值存放与之对应的字符的ASCII码;
因为要拿下标来对应ASCII,而ASCII一共128个字符,所以开辟的数组大小为128:

int[] sTot = new int[128];
int[] tTos = new int[128];

因为s.charAt(i)放到数组的下标处就会自动转换为ASCII码,所以数组实际使用时如下:

  • sTot[s.charAt(i)]表示s上第i个字符对应在t上的第i个字符的ASCII;
  • tTos[t.charAt(i)]表示t上第i个字符对应在s上的第i个字符的ASCII;

如果sTot[s.charAt(i)] != t.charAt(i),说明s->t的映射是不合法的;同理,
如果tTos[t.charAt(i)] != s.charAt(i),说明t->s的映射是不合法的。

知识点:
1、char字符写在数组的下标处时会自动转换为ASCII码值。
2、int数组存放char型数据时,char型数据会自动转成ASCII码存入。
实现代码
class Solution {
    public boolean isIsomorphic(String s, String t) {
        int[] sTot = new int[128];//记录s->t的映射关系
        int[] tTos = new int[128];//记录t->s的映射关系
        for(int i = 0; i < s.length(); i++){
            int sCh = s.charAt(i), tCh = t.charAt(i);
            if(sTot[sCh] == 0) sTot[sCh] = tCh;
            else if(sTot[sCh] != tCh) return false;
            if(tTos[tCh] == 0) tTos[tCh] = sCh;
            else if(tTos[tCh] != sCh) return false;
        }
        return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值