【问题标题】:String anagrams C program [duplicate]字符串字谜C程序[重复]
【发布时间】:2012-10-11 11:08:03
【问题描述】:

可能重复:
What is an easy way to tell if a list of words are anagrams of each other?
finding if two words are anagrams of each other

我编写了下面的 C 代码来检查两个给定的字符串是否是彼此的字谜。 我知道这在复杂性/效率方面是最糟糕的,而且还有很多更好的方法可以做到这一点。

#include "stdio.h"

main()
{
char s1[]="mist";
char s2[]="mitt";
int i,j,isanag=0;

if(strlen(s1)!=strlen(s2))
    printf("Not anagrams\n");

for(i=0;i<strlen(s1);i++)
{
    isanag=0;
    for(j=0;j<strlen(s2);j++)
    {
        if(s1[i]==s2[j])
        {
            isanag = 1;
            break;
        }
    }
    if(isanag == 0)
    {
        printf("Not anagrams\n");
        getch();
        exit(0);
    }

}

printf("Yes Anagrams\n");
getch();
}

这可以正常工作并打印正确的 Not Anagrams 如果我如下交换两个字符串的名称,则会给出错误的答案

char s1[]="mitt";
char s2[]="mist";

我知道 2 个 for 循环的编码方式,这很明显。

我可以做些什么来改进这个代码并解决这个怪癖?

【问题讨论】:

  • 穷人main()返回类型你住哪里?
  • 您的算法是错误的,因为它只检查s1 的所有不同字符是否存在于s2 中,但 检查s2 中是否有字符不在s1 中。这就是为什么"mitt" 被报告为"mist" 的字谜; s2 中的 's' 被忽略。您的程序也无法检测到不同数量的重复字母,即"mistmt" vs "mist"
  • 您的算法可能是最差的。它的复杂度是O(n^2 * m^2),其中 n 和 m 是长度。查看 dup 以获得更好的答案。
  • @C2H5OH 你在盗用我的名字吗? :D
  • @KingsIndian:如果 n 和 m 是 2 个字符串的长度,我不认为这个复杂度是 O(n^2*m^2),我看到这是 O(n*m) .注意详细说明。

标签: c string


【解决方案1】:

仅考虑小写字母,您可以制作 2 个长度为 26 的向量。

将它们中的所有位置都设置为0,在第一个字符串(s1)中循环并增加向量中的位置:

   int v1[26], v2[26], i;
   for( i = 0; i < 26; i++)
        v1[i] = v2[i] = 0;
   for(i = 0; i < strlen(s1); i++)
        v1[s1[i] - 'a']++; // 'a' would be on position 0, 'b' on position 1 and so on
   for(i = 0; i < strlen(s2); i++)
        v2[s2[i] - 'a']++; 

之后你只需循环输入向量,看看字母的数量是否不同

   for(i = 0; i < 26; i++)
        if(v1[i] != v2[i])
        {
            printf("Not anagrams");
            exit(0);
        }
    printf("Anagrams");

但是如果你使用大写字母,你可以创建 4 个向量,新的向量减去 'A' 或创建一个更大的向量,然后在你的代码中添加一些 if ......我会让你试试那个 ;)

【讨论】:

  • 很好的解决方案。如果您在 256 中更改 26 并丢失 - 'a',则可以比较任何字节字符串,包括大写。
  • 您也可以使用size_t 作为计数类型,因为确实存在可以创建大于int 最大值的数组的系统。您不想将包含 2^32 个“a”的字符串报告为包含 2^32 个“b”(或空字符串)的字符串的字谜。
【解决方案2】:

基于 vmp 的解决方案,您可以使用一个字符数组 [26] 来完成此操作。

  1. 遍历第一个字符串,增加数组元素 一封信。
  2. 遍历第二个字符串,递减数组 一个。
  3. 遍历字母数组并断言所有元素都是 零。

编辑:添加了一些代码(手头没有编译器,但概念应该没问题)

//lower case only
int isAnagram( char* left, char* right)
{
   char theAlphabet[26];

   memset( theAlphabet, 0, sizeof theAlphabet );


   int length = strlen( left );

   for( int i=0; ++i; i < length )
   {
      if ( 0 == right[i] )
      { //mismatching length
        return 0; 
      }

     ++theAlphabet[ left[i] - 'a' ];
     --theAlphabet[ right[ i ] - 'a' ];

   }

   if ( left[length] != 0
       || right[length] != 0 )
   {
     return 0;
   }


   for( int j=0; ++j; j < 26 )
   {
      if ( 0 != theAlphabet[j] )
      {
        return 0;
      }
   }

   return 1; //yes it is an anagram
}

【讨论】:

  • 或者在一个循环中组合1+2,当然... O(2n) 应该是结果...
【解决方案3】:

@Goldenmean,这是对两个字符串进行排序然后比较它们的另一种解决方案。其他人讨论的字符计数会更有效,但这更有趣:-)

qsort 是一个标准库排序函数,它将函数 comp 作为参数,并在对列表重新排序时使用它来比较列表(在本例中为字符串)的元素。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static int comp(const void *a, const void *b)
{
    const char *pa = a;
    const char *pb = b;

    return
        (*pa > *pb) ?  1 :
        (*pa < *pb) ? -1 :
        0;
}

int main(int argc, char ** argv)
{
    char s1[]="mits";
    char s2[]="mist";

    qsort(s1, strlen(s1), 1, comp);
    qsort(s2, strlen(s2), 1, comp);

    printf("%s : %s  - %s\n", s1, s2, strcmp(s1, s2) ? "No" : "Yes");
    return 0;
}

【讨论】:

  • "这更有趣" +1 因为除了更有趣之外,它没有对字母表做任何特定的假设(好吧,对于 Unicode 字母表,它假设规范化)。 “仅适用于小写字符串并假设 ASCII 以使 s[i] - 'a' 在 0 到 25 范围内)”是一个很好的 hack,但是有一个相当快速和更通用的后备是很好的。
【解决方案4】:

你没有编码重复字母的可能性。

取这两个字符串:

char s1[]="mitt";
char s2[]="mist";

对于第一个字符串中的每个字母,您的代码会检查第二个字符串中的每个字母,以查看在任何地方是否有相同的字母。因此,让我们检查第一个字符串并检查第二个字符串中的 第一个相同字母(这就是您的代码所做的):

s1[0] ('m'): yes, at s2[0]
s1[1] ('i'): yes, at s2[1]
s1[2] ('t'): yes, at s2[3]
s1[3] ('t'): yes, at s2[3]

如您所见,代码认为这两个字符串是字谜,因为它将第一个字符串中的两个字母匹配到第二个字符串中只有一个字母

解决方案是在进入下一个字母之前“删除”已经匹配的字母;我会把它留给你编码!祝你好运。

编辑:当然,我忘记了:如果代码成功地一直通过字符串 1,从字符串 2 中删除字母,并且字符串 2 中还有字母,这意味着它们不是字谜! (在上面的示例中,'s' 将留在字符串 2 中。)

【讨论】:

    【解决方案5】:

    很抱歉,您的实施存在缺陷。这段代码:

    for(j=0;j<strlen(s2);j++)
    {
        if(s1[i]==s2[j])
        {
            isanag = 1;
            break;
        }
    

    只要求第二个字符串中的 any 字符出现在第一个字符串中。

    因此,由于“mitt”的字母都在“mits”之内,并且长度相同,因此报告它们是字谜。反之则不然。

    即使你在另一个方向重复检查,这仍然行不通。

    例如

    mitttsson
    
    missstton
    

    出现为字谜,因为它们的长度相同,并且都是由集合 {m,i,t,s,o,n} 生成的。

    您不仅要检查字符是否相等,还要检查它们在字符串中出现的次数。

    这是一种(效率低下,因为它会重复计算重复的字母)变体:

    for (i = 0; i < strlen(s1); i++)
    {
        int c = 0;
        // How many times does character s1[i] occur in s1?
        for (j = 0; j < strlen(s1); j++)
        {
            if (s1[i] = s1[j])
            {
                // Improvement: if j < i, it means we already checked this letter.
                // so we might break here...
                c++;
            }
        }
        // improvement: if c is 0 here, it means we can 'continue' for this letter
        // has been already checked before.
    
        // Subtract how many times it occurs in s2.
        for (j = 0; j < strlen(s2); j++)
        {
            if (s1[i] = s2[j])
                c--;
        }
        // If occurrences are the same we expect difference to be 0.
        if (c)
        {
            printf("Not anagrams\n");
            break;
        }
    }
    

    更新:最好的解决方案是完全改变算法,根据 vmp 或(甚至更好)Mario the Spoon 的。

    【讨论】:

      猜你喜欢
      • 2016-03-21
      • 2015-08-29
      • 2020-04-15
      • 2014-01-19
      • 2013-03-04
      • 2019-01-01
      • 2016-06-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多