关于判断同构字符串的C++代码中for循环逻辑的疑问
同构字符串判断代码全解析
一、先明确:什么是同构字符串
两个字符串是同构的,得满足两个核心要求:
- s里的每个字符,能唯一对应t里的一个字符;反过来t里的每个字符,也得唯一对应回s里的原字符
- 举例子:
"egg"和"add"是同构的(e→a,g→d);但"foo"和"bar"不是(o既要对应a又要对应r,冲突了)
二、关于字符串长度的疑问
这段代码默认传入的s和t长度相同,如果实际长度不一样:
- 要是s比t长,循环到t的长度后访问
t[i]会直接越界报错 - 要是t比s长,循环只跑s的长度,根本验证不到t后面的字符
所以严谨写法应该先加一句判断:
if(s.length() != t.length()) return false;
三、核心:map<char, int>的交互逻辑
先记住C++ map的一个关键特性:访问不存在的键时,会自动插入这个键,并且给它赋值为对应类型的默认值——这里int的默认值是0,这就是你打印能看到0的原因。
循环里的判断和赋值到底在干嘛?
循环逐个遍历每个字符的位置i(从0开始):
判断条件
if(mapS[s[i]] != mapT[t[i]]) return false;
这个判断是在验证:s中当前字符s[i]的「标记值」,和t中对应位置t[i]的「标记值」是否一致。
这个标记值的作用是记录字符出现的“同步性”:- 第一次出现的字符,标记值都是0(map自动生成的默认值),所以相等,能继续
- 如果s里的某个字符重复出现,那它的标记值应该和t里对应位置的字符标记值完全一样——比如s里第2个位置是之前出现过的g,那t里第2个位置必须是之前和g对应的d,否则标记值就会不一样,直接返回false。
举个实际例子(s="egg",t="add"):
- i=0:mapS里没有'e',
mapS['e']=0;mapT里没有'a',mapT['a']=0,相等,继续 - i=1:mapS里没有'g',
mapS['g']=0;mapT里没有'd',mapT['d']=0,相等,继续 - i=2:mapS['g']已经是2(i=1时赋值的i+1=2),mapT['d']也是2,相等,继续,最后返回true
反例(s="foo",t="bar"):
- i=0:mapS['f']=0,mapT['b']=0,相等,赋值为1
- i=1:mapS['o']=0,mapT['a']=0,相等,赋值为2
- i=2:mapS['o']=2,mapT['r']=0,2≠0,直接返回false,正确识别非异构
赋值操作
mapS[s[i]] = i+1; mapT[t[i]] = i+1;
用i+1而不是i,是为了区分「从未出现过的字符(默认0)」和「在位置0出现的字符」——如果用i的话,位置0的字符赋值为0,和未出现的字符默认值一样,会导致判断出错。
每次赋值都会把字符的标记值更新为当前位置+1,这样后续如果这个字符再出现,就能通过标记值和t中对应字符的标记值对比,验证映射关系是否一致。
四、为什么打印会得到0和1?
- 当你打印
mapS[s[i]]时,如果这个字符是第一次被访问,map会自动插入该键,值为int的默认值0,这就是你看到0的原因 - 执行赋值语句后,这个值会变成
i+1,i从0开始,所以第一次赋值后就是1,这就是你看到1的原因
五、代码优化建议
map是有序容器,查询和插入的时间复杂度是O(logn),可以换成更高效的unordered_map(平均O(1)),或者直接用数组(因为char的ASCII值范围是0-255,数组访问是O(1)):
class Solution { public: bool isIsomorphic(string s, string t) { if(s.length() != t.length()) return false; int mapS[256] = {0}; int mapT[256] = {0}; for(int i = 0; i < s.length(); i++) { if(mapS[s[i]] != mapT[t[i]]) return false; mapS[s[i]] = i+1; mapT[t[i]] = i+1; } return true; } };
内容的提问来源于stack exchange,提问作者Wayne Miles
相关产品推荐
相关产品推荐

