关于LeetCode中Isomorphic Strings一题的理解

本文介绍了一种算法,用于判断两个字符串是否为同构字符串。通过使用哈希表和ASCII码特性,提供了几种高效的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目如下:

Given two strings s and t, determine if they are isomorphic.

Two strings are isomorphic if the characters in s can be replaced to gett.

All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character but a character may map to itself.

For example,
Given "egg", "add", return true.

Given "foo", "bar", return false.

Given "paper", "title", return true.

Note:
You may assume both s and t have the same length.

    题目要求给定两个字符串,编写程序来判断这两个字符串是不是同构的,如果是同构的返回true,否则返回false。什么样的两个字符串是“同构”的呢?题干中给了我们几个例子,比如字符串"egg"和"add"就是同构的,因为将"e"替换成"a"后,两个"g"同时替换成两个"d"后,这两个字符串就变成相同的了,所以他们是"同构"的。我们再看一个例子"123"和"333",这两个字符串肯定不是"同构"的,但是有人说把"1"变成“3”,"2"变成"3"之后不就变成了"333"吗?但问题是,"333"这个字符串是不可能同时将两个"3"转化成"1"和"2"的,因为题干中说道要转换所有相同的字符就要跟着一块儿转换,所以只能把"3"全部变成"2",或者全部变成"1",不存在一个变成"1",一个变成"2"的情况,这一点我们需要明确。下面来说一下思路,最开始做的时候我就想找到这两个字符串分别对应的“模式串”,然后在比较这两个模式串是否相等,如果相等说明“同构”,反之则不同构。我所设计的这个模式串其实就是一个数组,数组的下标对应着在给定字符串中那个下标位置出现的字符,数组中的值是该字符上一次出现的位置,这样一个初步的“模式串”就完成了,但是问题也随之而来,记录上一次出现的位置就需要从字符当前位置向前回溯,这又涉及到一个循环,加上外部还有一个指针后移的循环,那最后的就变成了“双重循环”,这个时间复杂度就变成了O(n的平方),如果字符串过长的话,程序就会超时,所以这种找位置的思路被pass了。这种方式行不通,但是我们还是需要用到“模式串”,怎样能快速找到这个字符在之前出现的位置呢?答案当然是使用Hash表,具体来说就是使用HashMap,将字符作为key值,第一次出现的位置作为value值,这样我们就能很轻易且快速地得到我们的一个“模式串”。同理,另一个模式串也可以同时得到,我们直接将这两个不同“模式串”中同位置的元素值进行比较就可以,相同则继续,不同则直接返回false。已Accepted的代码如下:

<span style="font-size:14px;">    public boolean isIsomorphic(String s, String t) {
        if(s.length()!=t.length()){
            return false;
        }
        if(s.length() == 0){
            return true;
        }
        
        char[] s_array = s.toCharArray();
        char[] t_array = t.toCharArray();
        
        int[] mod = new int[s_array.length];
        int[] mod2 = new int[t_array.length];
        
        HashMap<String,Integer> comparedMap = new HashMap<String,Integer>();
        HashMap<String,Integer> comparedMap2 = new HashMap<String,Integer>();
        
        for(int i=0;i<s_array.length;i++){
            String key = String.valueOf(s_array[i]);
            String key2 = String.valueOf(t_array[i]);
            if(!comparedMap.containsKey(key)){
                comparedMap.put(key,i);
                mod[i] = i;
            }else{
                mod[i] = comparedMap.get(key).intValue();
            }
            
            if(!comparedMap2.containsKey(key2)){
                comparedMap2.put(key2,i);
                mod2[i] = i;
            }else{
                mod2[i] = comparedMap2.get(key2).intValue();
            }
            
            if(mod[i]!=mod2[i]){
                return false;
            }
        }
        return true;
    }</span>
    然后是评论区的部分,第一中方法和我的思路一样,不过人家的炒鸡简练,代码如下:
<span style="font-size:14px;">    public boolean isIsomorphic(String s, String t) {
        Map m = new HashMap();
        for (Integer i=0; i<s.length(); ++i)
            if (m.put(s.charAt(i), i) != m.put(t.charAt(i)+"", i))
                return false;
        return true;
    }</span>
    另一种比较有意思,代码也是非常简练,而且速度很快,不过他的代码好像是C++的,我稍微修改了一下变成了java版本的,如下所示:
<span style="font-size:14px;">    public boolean isIsomorphic(String s, String t) {
        int[] m1 = new int[256];
        int[] m2 = new int[256];
        char s_char;
        char t_char;
        for(int i=0;i<s.length();i++){
            s_char = s.charAt(i);
            t_char = t.charAt(i);
            if(m1[s_char]!=m2[t_char]){
                return false;
            }
            m1[s_char] = i+1;
            m2[t_char] = i+1;
        }
        return true;
    }</span>
    首先声明了两个数组,大小都是256这是因为ASCII码表里字符串总共是256个。然后我们直接看循环里面,他每次比较的m1和m2这两个数组分别在s_char位置和t_char位置的值。这个值代表s_char这个字符和t_char这个字符在整个字符串中上一次出现的位置。如果两个字符串是同构的,那它们在进行每一次比较的时候得到的结果都是相等的,如果不相等肯定说明某个字符删一次出现的位置和另一个字符上一次出现的位置不同,说明这两个字符串肯定不同构,这和我第一次“模式串”的构造是类似的,都是记录上一次出现的位置,注意这里记录的是i+1位置,而不是i。这是因为初始数组中的值都是0,如果是i的话就区分不开上一次出现的位置在0号位和没出现过该字符的情况,因为没出现过的字符的值都是0。举个例子,如果每次赋值i而不是i+1,那么字符串"aa"和字符串"cd"就会出现误判,在第二次循环是由于m1['a']=0,而m2['d']=0,所以程序最后返回的是true,但是显然应该返回false。赋值变成i+1后,还是在第二次循环的时候,m1['a']=1,m2['d']=0,此时不相等,返回false,这一点是需要注意的。

    另一种方法也很不错,同样也是利用了ASCII码,这个还是先放一下代码:

<span style="font-size:14px;">    public boolean isIsomorphic(String sString, String tString) {

        char[] s = sString.toCharArray();
        char[] t = tString.toCharArray();

        int length = s.length;
        if(length != t.length) return false;

        char[] sm = new char[256];
        char[] tm = new char[256];

        for(int i=0; i<length; i++){
            char sc = s[i];
            char tc = t[i];
            if(sm[sc] == 0 && tm[tc] == 0){
                sm[sc] = tc;
                tm[tc] = sc;
            }else{
                if(sm[sc] != tc || tm[tc] != sc){
                    return false;
                }
            }
        }
        return true;
    }</span>
    关键说一下sm[sc] = tc; tm[tc]=sc;这两句,如果两个字符串都是第一次在这个位置遇到这两个字符,就将这两个字符交换存储,这样下次再遇到该字符时就可以通过查看sc和tm数组来判断,这两个字符是不是之前互为对应的,如果不是的话就直接返回false,也就是sm[sc]!=tc || tm[tc]!=sc结果为true的情况下执行的语句。这个解决方案还是非常smart的,比我屌多了!

    剩下的方法基本上也是大同小异,如果想继续了解了话可以直接查看评论区,就是这里:https://discuss.leetcode.com/category/213/isomorphic-strings


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值