两个字符串中的唯一字符对

Posted

技术标签:

【中文标题】两个字符串中的唯一字符对【英文标题】:Unique char pair in two strings 【发布时间】:2020-07-31 23:35:53 【问题描述】:

所以我需要一些锻炼帮助,我被困住了!放轻松,我对此很陌生。练习如下:

给定两个长度相等的 char 数组,确定第一个数组中的每个字符是否可以唯一地替换为第二个数组中的字符,以使两个数组相等。显示两个数组之间的字符对。

示例 1: 给定以下输入:aabttd,控制台将显示:

True
a => t
b => d

示例 2: 给定以下输入:abattd,控制台将显示:

False

在第二个示例中,答案是 False,因为字符 a 没有唯一的替换符:td 都对应。

我一直在为此烦恼,老实说,我觉得我什至无法再思考了。已经对代码进行了很多次调整,以至于我几乎不再理解它了。虽然它仍然通过了第一个测试,但似乎一直未能通过更长的字符串输入的测试。例如输入:ala bala portocalacuc dcuc efghijcuc,控制台显示True,然后是对,这是不对的。下面你有我的 POS 码,欢迎任何建议。

static void Main(string[] args)
    
        string a = Console.ReadLine();
        string b = Console.ReadLine();

        string one = string.Empty;
        string two = string.Empty;

        bool res = false;
        int count = 0;

        for (int i = 0; i < a.Length; i++) 
        
            if (!one.Contains(a[i].ToString()))
            
                if (!two.Contains(b[i].ToString())) 
                
                    one += a[i];
                    two += b[i];
                
            
        

        char[] firstPhrase = new char[one.Length];
        char[] scndPhrase = new char[two.Length];

        for(int i = 0; i < one.Length; i++) 
        
            bool temp = false;
            for(int j = 0; j < two.Length; j++) 
            
                if(firstPhrase[j] != one[i]) 
                
                    for(int k = 0; k < scndPhrase.Length; k++) 
                    
                        if(scndPhrase[j] == two[i])  res = true; break; 

                    
                    if(res == true)  break; 
                    else  continue; 
                
                if(firstPhrase[j] == one[i]) 
                
                    if (scndPhrase[j] == two[i])  temp = true; continue; 
                    else  res = true; break; 
                

            
            if(temp == false) 
            
                firstPhrase[count] = one[i];
                scndPhrase[count] = two[i];
                count++;
            
            if(res == true)  break; 
        


        if (res == true)
            Console.WriteLine(res);
        else
        
            Console.WriteLine(!res);
            for (int i = 0; i < firstPhrase.Length; i++)
            
                Console.WriteLine($"firstPhrase[i] => scndPhrase[i]");
            
        
        Console.Read();
    

期待您的批评,祝大家复活节快乐!

编辑:忘了提,对不起各位。测试不允许使用 LINQ 或任何其他指令,LINQ 实际上是我想尝试的第一件事。

【问题讨论】:

您是否可能对作业理解有误?因为它在最后说:“所以两个数组相等”并在第一个示例中将 a 替换为 t 并将 b 替换为 d 为不相等的数组提供以下内容:ttd, aab? 你老师有没有把这个作为第一个需要通过的测试? 嘿 Oguz,是的,我认为翻译中可能会丢失一些内容。即使在母语中,它的措辞也有些奇怪。您实际上不必替换字符,替换是假设的。关键是确定每个字符是否可以在另一个数组中有唯一的替换器,然后映射那些唯一的替换器。 不,第一个测试是使用以下输入:“ala bala portocala”和“cuc dcuc efghficuc”,并且代码通过了它。虽然没有超过第二个.. 我赞成这两个答案,因为它们都解决了问题。我认为需要 15 个代表才能直接看到投票。 【参考方案1】:

这是一个仅使用 System 命名空间的解决方案。

这里的重要部分是先将两个数组中的字符都更改为标记。

这意味着,对于“ala bala portocala”,我们将拥有:

 0, 1, 0, 2, 3, 0, 1, 0, 2, 4, 5, 6, 7, 5, 8, 0, 1, 0 

所以每当找到一个新字符(以前没有出现过)时,我们都会将数字加 1。

您可以看到,从这个有趣的角度来看,这可以很好地评估两个 char 数组是否匹配。

static int[] Tokenize(char[] array)

    int length = array.Length;
    int[] distinctArray = new int[length];
    int offset = 0;
    for (int i = 0; i < length; i++)
    
        bool appearedBefore = false;
        for (int j = 0; j < i; j++)
        
            if (array[j] == array[i])
            
                appearedBefore = true;
                distinctArray[i] = distinctArray[j];
                break;
            
        
        if (!appearedBefore)
        
            distinctArray[i] = offset;
            offset++;
        
    
    return distinctArray;

然后是测试方法:

static void Test(char[] array1, char[] array2)

    Console.WriteLine("[0] VS [1]", new string(array1), new string(array2));

    int[] array1Tokenized = Tokenize(array1);
    int[] array2Tokenized = Tokenize(array2);

    for (int i = 0; i < array1.Length; i++)
    
        if (array1Tokenized[i] != array2Tokenized[i])
        
            Console.WriteLine("False");
            return;
        
    

    Console.Write("True");
    for (int i = 0; i < array1.Length; i++)
    
        bool appearedBefore = false;
        for (int j = 0; j < i; j++)
        
            if (array1[j] == array1[i])
            
                appearedBefore = true;
                break;
            
        
        if (!appearedBefore && array1[i] != array2[i])
        
            Console.Write(" 0 => 1", array1[i], array2[i]);
        
    
    Console.WriteLine();

结果:

static void Main(string[] args)

    Test("ala bala portocala".ToCharArray(), "cuc dcuc efghijcuc".ToCharArray());

    Test("ala bala portocala".ToCharArray(), "cuc dcuc efghfjcuc".ToCharArray());

    Test("aab".ToCharArray(), "ttd".ToCharArray());

输出:

[ala bala portocala] VS [cuc dcuc efghijcuc]
False

[ala bala portocala] VS [cuc dcuc efghfjcuc]
True a => c l => u b => d p => e o => f r => g t => h c => j

[aab] VS [ttd]
True a => t b => d

【讨论】:

绝对可以从您的解决方案中学到很多东西,非常感谢您抽出宝贵时间帮助我!我感谢所有 cmets 和解决方案。请注意安全,祝您有个愉快的夜晚! 我猜可以变得更简单。一次通过 o(log n) 解决方案似乎是可能的【参考方案2】:

这是一个仅使用 System 命名空间的简短解决方案。基本上,我们需要做的就是并行遍历字符串一次,并为每个位置检查每个字符串中的字符是否相互映射。分三种情况:

    字符之前没有被映射,此时我们将它们相互映射; 字符已经相互映射,在这种情况下我们继续; 其中一个字符已经映射,但没有映射到另一个字符,在这种情况下没有有效的解决方案,所以我们返回 null。

为了跟踪映射,我们使用两个并行数组,mappingsreverseMappings。由于字符只是数字,我们可以使用第一个字符的值作为映射数组的索引,并将该值设置为等于第二个字符。反向映射也是一样,但字符相反。 (只要映射数组足够大,可以处理我们期望的每个可能的字符值,这将起作用。对于 ASCII,128 是所需的最大大小。对于 Unicode,它需要是 65536。)我们继续计数到目前为止我们已经看到了多少对。这是必要的,以便我们知道返回数组(数组的 n×2 数组)有多大,它将包含输出所需的实际字符对。我们通过扫描一次映射数组来构造它,查找所有具有非零值的映射。

public char[][] GetCharacterMappings(string s1, string s2)

    if (s1 == null || s2 == null || s1.Length != s2.Length) return null;

    // I'm assuming the inputs can only be in the ASCII character set;
    // If they can have any Unicode character, make this value larger (e.g. 65536) 
    const int maxCharValue = 128;
    char[] mappings = new char[maxCharValue];
    char[] reverseMappings = new char[maxCharValue];
    int pairCount = 0;

    for (int i = 0; i < s1.Length; i++)
    
        char c1 = s1[i];
        char c2 = s2[i];

        if (mappings[c1] == 0 && reverseMappings[c2] == 0)
        
            // these characters have not been mapped before
            mappings[c1] = c2;
            reverseMappings[c2] = c1;
            pairCount++;
        
        else if (mappings[c1] != c2 || reverseMappings[c2] != c1)
        
            // either character is already mapped to some other character
            return null;
        
    

    char[][] pairs = new char[pairCount][];
    int p = 0;
    for (int i = 0; i < maxCharValue; i++)
    
        if (mappings[i] != 0)
            pairs[p++] = new char[]  (char)i, mappings[i] ;
    

    return pairs;

输出例程只检查配对是否为空,如果不是,则将它们全部转储:

public void WriteResult(char[][] pairs)

    Console.WriteLine(pairs != null);
    if (pairs != null)
    
        for (int i = 0; i < pairs.Length; i++)
        
            Console.WriteLine(pairs[i][0] + " => " + pairs[i][1]);
        
    

这是一个工作演示:https://dotnetfiddle.net/lBHKUY

【讨论】:

@Downvoter,愿意评论我如何改进我的答案吗?【参考方案3】:

这是一个使用 LINQ 的解决方案:

var result = str1.Zip(str2, (f, s) =>  f.ToString() + s.ToString())
                 .GroupBy(c => c[0])
                 .Where(c => c.Select(d => d[1]).Distinct()
                 .Skip(1).Any()).Any() ? false : true;

您只需将using System.Linq 添加到您的 using 指令即可。

您可以在这里在线查看结果:https://dotnetfiddle.net/OF2cnO

【讨论】:

很抱歉,原帖中忘记提及了,现在编辑一下。该测试不允许使用系统以外的任何东西。 Linq 会是我的第一次尝试。 @DaveLancaster 你是什么意思?您可以简单地将using System.Linq 添加到您的 using 指令中,它不需要任何特殊的东西,.NET 默认支持它。 平台不允许使用Linq或System以外的任何其他指令,一开始用Linq尝试过,但平台因此拒绝编译。 看起来很棒,萨拉赫!戴夫不会赞成它,但我会.. 并对其进行测试.. 你正在使用我不知道的 Linq 的一些功能。有趣的答案。 嘿,但我确实赞成它.. 还没有足够的声誉,所以它不会被显示【参考方案4】:

考虑使用 .NET 字典。当下面的函数返回 null 时,没有一致的映射。当它返回一个字典时,它的内容将是映射。我编译了这个,我没有测试它..

public static Dictionary<char, char> BuildMapping(string s1, string s2)

    if (s1.Length != s2.Length) return null;
    Dictionary<char, char> result= new Dictionary<char, char>();
    for (int i = 0; i < s2.Length; i++)
    
        if (result.ContainsKey(s1[i]))
            if (result[s1[i]] == s2[i]) continue;
            else return null;
        result[s1[i]] = s2[i];
    
    return result;

【讨论】:

您好,非常感谢您的回答。修改了原帖,不好意思。测试不允许使用系统以外的任何其他指令。 好吧..也许这是一个很好的子任务,然后:实现一个存储 char-char 映射的类并将其称为“字典”。顺便说一句.. 告诉你的老师,今天没有程序员从零库开始。当您想学习 C# 时,.net 的附加知识是必不可少的。您不必承认我的回答,请点赞.. thx 好点,我也很惊讶他们不允许使用其他库。这个阶段的重点是算法分配,我猜他们不允许使用其他指令,因为它会使任务变得太容易。不过谢谢你的建议,今晚我会试一试,看看结果如何。非常感谢您的意见。

以上是关于两个字符串中的唯一字符对的主要内容,如果未能解决你的问题,请参考以下文章

100天算法入门 - 每日三题 - Day14两个数组的交集有效的完全平方数字符串中的第一个唯一字符

java如何对一个字符串生成唯一的编码

如何找到包含两个唯一重复字符的最长子字符串

如何用两个字符串生成一个唯一的数字

如何多次替换两个分隔符/字符串之间的唯一字符串

java对两个字符串数组取交集并集和差集