如何证明两个字符串是彼此的排列?

Posted

技术标签:

【中文标题】如何证明两个字符串是彼此的排列?【英文标题】:How to prove that two strings are permutations of each other? 【发布时间】:2014-08-01 01:57:03 【问题描述】:

我正在破解代码面试书,我在数组和字符串一章中遇到了一个问题,他们要求编写一个方法来证明作为输入给出的两个字符串是彼此的排列。

书中的答案非常简洁明了。一种是排序,然后比较是否相同,另一种是检查两个字符串的字符数是否相同。

但是,我对这个问题有不同的方法,我想和你分享一下,看看你的意见。

我假设字符是 ASCII 字符。 所以我想到的是首先检查两个字符串的长度是否相等,如果不相等我们直接返回 false,因为它显然与排列的定义相反。

如果是这种情况,我们继续算法。首先,我们初始化:

int sum = 0;
int sum1 = 0;

然后我们遍历每个字符串的字符,将每个字符的 ASCII 值加到总和中,最后比较总和。如果它们相等,那么我们就得到了一个排列。

这种方法有效吗?

【问题讨论】:

相反,还有其他方法。您可以简单地按字典顺序对字符串进行排序并按字符进行比较。另一种方法是找到两个字符串的每个字符的频率,然后比较它们。 【参考方案1】:

不,它不起作用,因为12 既是210 的总和,也是39 的总和。

使用您的算法"ad" 将是"bc" 的排列。

在一般情况下,如果您允许合理的字符范围和字符串长度,则没有真正的捷径。您提到的两者中最好的解决方案取决于语言。

【讨论】:

太快了,谢谢,还有 13 分钟,直到我给你一张绿色支票【参考方案2】:

毁灭是对的

要让它以 99.999% 的正确率(通过您的方法)工作,您需要计算:

sum1 = sum (ASCII(i))
sum2 = sum (ASCII(i)^2)
sum3 = sum (ASCII(i)^3)
对于两个字符串,如果所有相同的幂和相同 那么你有最有可能置换字符串...

确保比较直方图(正如您在问题中提到的那样),但这需要更多内存......

【讨论】:

【参考方案3】:

您的方法不起作用,因为总和会有很多冲突,即基本上您假设的是 5+3 = 8 并且没有其他组合会产生 8 但您错了示例 4 + 4 也是8.

有很多 Ad hoc 方法可以解决这个问题,我将描述其中的两个。 您可以使用与您的方法类似的方法来使用素数来解决问题,或者简单地分配 2 个数组并记录字符。

1. 您可以初始化 2 个大小为 27 的整数数组,每个数组说 list1[27] 和 list2[27] 初始化为 0 ,如果您读取 'c' 则逐个字符地读取两个字符串从字符串 1 开始,增加 list1 的第三个元素,因为 'c' 是第三个字符,依此类推,当您完成读取两个字符串时,如果有任何不匹配,则扫描两个数组是否不匹配,它们不是彼此的排列。

一个可能的实现可以是

char str1[50]="permutation";
char str2[50]="importunate";

int list1[27]=0,list2[27]=0;


for(int i=0;i<11;i++)
    list1[(int)str1[i]-(int)'a'+1]++;
    list2[(int)str2[i]-(int)'a'+1]++;


for(int i=0;i<=27;i++)
    if(i==27)
        return true;
    
    if(list1[i]!=list2[i])
    
        return false;
    

这个方法可以很容易地扩展到考虑空格、不同大小写字符和数字。

2. 此方法与您所做的类似,但不是使用 ASCII 值,而是使用素数,而不是加法,而是使用乘法。您的方法的问题是很多可能的冲突,因为损坏指出如果您选择相乘,您将再次面临同样的问题,但是如果我们不将 ascii 值相乘,而是将分配给特定字符的素数相乘,该怎么办。

在这里,我们首先分配一个数组,该数组存储从 2 开始的前 26 个素数,并逐个字符地读取字符串并将分配给字符串每个字符的所有相应素数相乘,最后比较两个大整数,如果那些相等则字符串是彼此的排列

一个可能的实现可能是

int arr[27]=2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103;
char str1[50]="permutation";
char str2[50]="importunate";

int prd1=1,prd2=1;


for(int i=0;i<11;i++)
    prd1=prd1*arr[(int)str1[i]-(int)'a'];
    prd2=prd2*arr[(int)str2[i]-(int)'a'];


if(prd1==prd2)
    return true;

else
    return false;

这个方法不像第一个方法那样可扩展,因为数字随着字符串的长度而变大, 我们可以

prd1=prd1*arr[(int)str1[i]-(int)'a']%1000000009;
prd2=prd2*arr[(int)str2[i]-(int)'a']%1000000009;//or some other large prime number 

【讨论】:

【参考方案4】:

这不能使用 sum 来实现,因为数字没有唯一的和因子(如前面的答案所述)

这可以通过比较字符直方图来完成

代码 Java

class Character_Histogram

    public Map<Character,Integer> histogram;

    public Character_Histogram ()
    
        histogram = new TreeMap<Character,Integer> ();
    

    public void count (Character c)
    
        if (histogram.containsKey(c))
            histogram.put(c, histogram.get(c)+1);
        else
            histogram.put(c, 1);
    

    public void count (String str)
    
        for(char c : str.toCharArray())
            count(new Character(c));
    

【讨论】:

问题在于是否可以使用 sum 进行比较。不要找到另一种比较方法,顺便说一句,他已经声明他知道如何计算这个(直方图 = 唯一字符数)

以上是关于如何证明两个字符串是彼此的排列?的主要内容,如果未能解决你的问题,请参考以下文章

使用基本Java检查两个字符串是不是是彼此的字谜[重复]

一种可能的算法来确定两个字符串是不是是彼此的字谜? [关闭]

如何交错或创建两个字符串的唯一排列(无递归)

如何在 BigQuery SQL 中将字符串列拆分为多行单个单词和单词对?

在 C++ 中检查一个字符串是不是是另一个字符串的排列

字谜递归Scala