从字符数组中删除字符

时间:2014-10-20 07:57:36

标签: c# .net arrays

有没有办法从当前字符数组中删除字符,然后将其保存到新的字符数组中。以下是代码:

string s1 = "move";
string s2 = "remove";
char[] c1 = s1.ToCharArray();
char[] c2 = s2.ToCharArray();

for (int i = 0; i < s2.Length; i++)
{
  for (int p = 0; p < s1.Length; p++)
  {
    if (c2[i] == c1[p])
    {
      // REMOVE LETTER FROM C2
    }


    // IN THE END I SHOULD JUST HAVE c3 = re (ALL THE MATCHING CHARACTERS M-O-V-E SHOULD BE
       DELETED)                               

非常感谢您的帮助

3 个答案:

答案 0 :(得分:0)

这不是特别有效,但对于短字符串来说可能足够快:

string s1 = "move";
string s2 = "remove";

foreach (char charToRemove in s1)
{
    int index = s2.IndexOf(charToRemove);

    if (index >= 0)
        s2 = s2.Remove(index, 1);
}

// Result is now in s2.

Console.WriteLine(s2);

这可以避免转换为char数组。

但是,只是为了强调:对于大字符串来说这将非常慢。

[编辑]

我做了一些测试,事实证明这段代码实际上非常快。

这里我将代码与另一个答案的优化代码进行比较。但请注意,我们并没有完全公平地比较,因为这里的代码正确地实现了OP的要求而其他代码没有。但是,它确实证明了HashSet的使用并没有人们想象的那么多。

我在发布版本上测试了这段代码,而不是在调试器中运行(如果你在调试器中运行它,它会进行调试构建,而不是发布版本会产生不正确的时序)。

此测试使用长度为1024的目标字符串和字符删除== "SKFPBPENAALDKOWJKFPOSKLW"

我的结果,其中test1()是另一个答案中不正确但据称优化的解决方案,test2()是我未经优化但正确的解决方案:

test1() took 00:00:00.2891665
test2() took 00:00:00.1004743

test1() took 00:00:00.2720192
test2() took 00:00:00.0993898

test1() took 00:00:00.2753971
test2() took 00:00:00.0997268

test1() took 00:00:00.2754325
test2() took 00:00:00.1026486

test1() took 00:00:00.2785548
test2() took 00:00:00.1039417

test1() took 00:00:00.2818029
test2() took 00:00:00.1029695

test1() took 00:00:00.2727377
test2() took 00:00:00.0995654

test1() took 00:00:00.2711982
test2() took 00:00:00.1009849

如您所见,test2()始终优于test1()。即使字符串增加到8192长度,这仍然是正确的。

测试代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Demo
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            var sw = new Stopwatch();

            string text = randomString(8192, 27367);
            string charsToRemove = "SKFPBPENAALDKOWJKFPOSKLW";

            int dummyLength = 0;

            int iters = 10000;

            for (int trial = 0; trial < 8; ++trial)
            {
                sw.Restart();

                for (int i = 0; i < iters; ++i)
                    dummyLength += test1(text, charsToRemove).Length;

                Console.WriteLine("test1() took " + sw.Elapsed);

                sw.Restart();

                for (int i = 0; i < iters; ++i)
                    dummyLength += test2(text, charsToRemove).Length;

                Console.WriteLine("test2() took " + sw.Elapsed);

                Console.WriteLine();
            }
        }

        private static string randomString(int length, int seed)
        {
            var rng = new Random(seed);
            var sb = new StringBuilder(length);

            for (int i = 0; i < length; ++i)
                sb.Append((char) rng.Next(65, 65 + 26*2));

            return sb.ToString();
        }

        private static string test1(string text, string charsToRemove)
        {
            HashSet<char> excludeCharacters = new HashSet<char>(charsToRemove);
            StringBuilder sb = new StringBuilder();

            foreach (char ch in text)
            {
                if (!excludeCharacters.Contains(ch))
                {
                    sb.Append(ch);
                }
            }

            return sb.ToString();
        }

        private static string test2(string text, string charsToRemove)
        {
            foreach (char charToRemove in charsToRemove)
            {
                int index = text.IndexOf(charToRemove);

                if (index >= 0)
                    text = text.Remove(index, 1);
            }

            return text;
        }
    }
}

[编辑2]

这是一个更加优化的解决方案:

public static string RemoveChars(string text, string charsToRemove)
{
    char[] result = new char[text.Length];
    char[] targets = charsToRemove.ToCharArray();
    int n = 0;
    int m = targets.Length;

    foreach (char ch in text)
    {
        if (m == 0)
        {
            result[n++] = ch;
        }
        else
        {
            int index = findFirst(targets, ch, m);

            if (index < 0)
            {
                result[n++] = ch;
            }
            else
            {
                if (m > 1)
                {
                    --m;
                    targets[index] = targets[m];
                }
                else
                {
                    m = 0;
                }
            }
        }
    }

    return new string(result, 0, n);
}

private static int findFirst(char[] chars, char target, int n)
{
    for (int i = 0; i < n; ++i)
        if (chars[i] == target)
            return i;

    return -1;
}

将其插入上面的测试程序会显示它的运行速度比test2()快3倍。

答案 1 :(得分:0)

您可以创建第三个数组c3,在其中添加c2中不要删除的字符。

您也可以使用Replace

string s3 = s2.Replace(s1,"");

答案 2 :(得分:0)

原始的O(N ^ 2)方法是浪费的。而且我不知道其他两个答案如何实际执行你似乎想要完成的工作。我希望这个具有O(N)性能的例子对你更有效:

string s1 = "move";
string s2 = "remove";
HashSet<char> excludeCharacters = new HashSet<char>(s1);
StringBuilder sb = new StringBuilder();

// Copy every character from the original string, except those to be excluded
foreach (char ch in s2)
{
    if (!excludeCharacters.Contains(ch))
    {
        sb.Append(ch);
    }
}

return sb.ToString();

当然,对于短串,性能不太重要。但恕我直言,这比其他选择更容易理解。

编辑:

我还不完全清楚OP在这里尝试做什么。最明显的任务是删除整个单词,但他的描述似乎都没有说出他真正想要的东西。因此,假设上述内容并未满足他的需求,但他也不想删除整个单词,这里还有其他几个选项......

1)O(N),对于非平凡长度的字符串的最佳方法,但有点复杂:

string s1 = "move";
string s2 = "remove";
Dictionary<char, int> excludeCharacters = new Dictionary<char, int>();

foreach (char ch in s1)
{
    int count;

    excludeCharacters.TryGetValue(ch, out count);
    excludeCharacters[ch] = ++count;
}

StringBuilder sb = new StringBuilder();

foreach (char ch in s2)
{
    int count;

    if (!excludeCharacters.TryGetValue(ch, out count) || count == 0)
    {
        sb.Append(ch);
    }
    else
    {
        excludeCharacters[ch] = --count;
    }
}

return sb.ToString();

2)O(N ^ 2)实现,至少最小化其他不必要的低效率,并且如果所有输入都相对较短就足够了:

StringBuilder sb = new StringBuilder(s2);

foreach (char ch in s1)
{
    for (int i = 0; i < sb.Length; i++)
    {
        if (sb[i] == ch)
        {
            sb.Remove(i, 1);
            break;
        }
    }
}

return sb.ToString();