修剪字符串中包含单个字符的重复字符

时间:2018-05-26 08:00:22

标签: c# .net regex string linq

这是一个采访问题 - 如何使用单个字符有效地修剪字符串中的重复字符。

实施例: 假设这是输入字符串

  

" reeeturrrnneedd"

输出应为:

  

"返回"

我通过使用分割字符串并循环遍历char数组来解释它,但是访问者不相信答案说这不是有效的方法。

private void test()
{
    string s = "reeeturrrnneeddryyf";
    StringBuilder sb = new StringBuilder();
    char pRvChar = default(char);
    foreach (var item in s.ToCharArray())
    {                
        if (pRvChar == item)
        {
            continue;
        }
        pRvChar = item;
        sb.Append(pRvChar);
    }

    MessageBox.Show(sb.ToString());
}

然后我想到Linq要反对并使用distinct但它会输出不正确的输出,因为它删除了所有重复的字符,输出将是" retund"

有人可以告诉我更有效的方法吗?

6 个答案:

答案 0 :(得分:6)

以下是正则表达式的解决方案:

Regex regex = new Regex( "(.)\\1+" );
string result = regex.Replace( s,"$1" );

我不确定,如果这样做更有效率,那么你的' for'在执行时间方面循环,但在开发人员工作方面更有效。 并且易于阅读,至少对熟悉正则表达式的人来说。

答案 1 :(得分:4)

如果您

,您当前的解决方案可能会更加优化
  1. 预先分配StringBuilder的容量,这在您的情况下是可能的,因为结果字符串与输入字符串的长度最多相同:

    StringBuilder sb = new StringBuilder(s.Length);
    
  2. 避免构造一个char数组来迭代。 string类会实现IEnumerable<char>,因此您可以直接将其反馈给foreach

    foreach (var item in s)
    

答案 2 :(得分:4)

比赛你的马!

以下是使用fixedunsafe预分配结果的指针版本

unsafe string Mine()
{
   var temp = string.Copy(Input);
   var i = 0;
   fixed (char* pInput = Input, pTemp = temp)
   {
      var plen = pInput + Input.Length;
      for (var pI = pInput + 1; pI < plen; pI++)
         if (*pI != *(pTemp+i))
            *(pTemp + ++i) = *pI;            
   }   
   return temp.Substring(0,i+1);
}

结果

Mode            : Release
Test Framework  : .NET Framework 4.7.1
Benchmarks runs : 100 times (averaged)

长度:100

Name     |  Average |  Fastest | StDv |  Cycles | Pass
---------------------------------------------------------
Mine     | 0.003 ms | 0.002 ms | 0.00 |   3,935 | Yes
Dmitry   | 0.003 ms | 0.002 ms | 0.00 |   5,455 | Yes
Billy    | 0.003 ms | 0.002 ms | 0.00 |   5,706 | Yes
SAkbari3 | 0.007 ms | 0.006 ms | 0.00 |  19,730 | Yes
SAkbari1 | 0.009 ms | 0.008 ms | 0.00 |  25,029 | Yes
Original | 0.009 ms | 0.003 ms | 0.05 |   7,349 | Base
SAkbari2 | 0.014 ms | 0.011 ms | 0.00 |  41,027 | Yes
Heinz    | 0.040 ms | 0.037 ms | 0.00 | 131,196 | Yes

长度:1,000

Name     |  Average |  Fastest | StDv |    Cycles | Pass
-----------------------------------------------------------
Mine     | 0.005 ms | 0.004 ms | 0.00 |    11,126 | Yes
Billy    | 0.008 ms | 0.007 ms | 0.00 |    22,402 | Yes
Dmitry   | 0.009 ms | 0.006 ms | 0.00 |    24,487 | Yes
Original | 0.010 ms | 0.008 ms | 0.00 |    27,334 | Base
SAkbari3 | 0.041 ms | 0.040 ms | 0.00 |   136,272 | Yes
SAkbari1 | 0.075 ms | 0.049 ms | 0.05 |   231,981 | Yes
SAkbari2 | 0.101 ms | 0.076 ms | 0.03 |   334,375 | Yes
Heinz    | 0.344 ms | 0.267 ms | 0.07 | 1,154,860 | Yes

长度:10,000

Name     |  Average |  Fastest | StDv |     Cycles | Pass
------------------------------------------------------------
Mine     | 0.020 ms | 0.017 ms | 0.00 |     62,571 | Yes
Dmitry   | 0.056 ms | 0.046 ms | 0.01 |    185,538 | Yes
Billy    | 0.061 ms | 0.058 ms | 0.00 |    202,931 | Yes
Original | 0.069 ms | 0.058 ms | 0.01 |    230,297 | Base
SAkbari3 | 0.419 ms | 0.372 ms | 0.09 |  1,418,448 | Yes
SAkbari1 | 0.535 ms | 0.452 ms | 0.09 |  1,813,644 | Yes
SAkbari2 | 0.957 ms | 0.726 ms | 0.19 |  3,226,844 | Yes
Heinz    | 2.951 ms | 2.574 ms | 0.47 | 10,027,205 | Yes

长度:100,000

Name     |   Average |   Fastest | StDv |     Cycles | Pass
--------------------------------------------------------------
Mine     |  0.164 ms |  0.158 ms | 0.01 |    552,166 | Yes
Dmitry   |  0.498 ms |  0.467 ms | 0.02 |  1,690,471 | Yes
Original |  0.561 ms |  0.523 ms | 0.06 |  1,894,019 | Base
Billy    |  0.576 ms |  0.536 ms | 0.04 |  1,955,072 | Yes
SAkbari3 |  3.684 ms |  3.429 ms | 0.15 | 12,534,942 | Yes
SAkbari1 |  4.547 ms |  4.084 ms | 0.47 | 15,468,091 | Yes
SAkbari2 |  7.315 ms |  6.848 ms | 0.30 | 24,888,849 | Yes
Heinz    | 26.091 ms | 24.898 ms | 1.17 | 88,905,648 | Yes

长度:1,000,000

Name     |    Average |    Fastest | StDv |      Cycles | Pass
-----------------------------------------------------------------
Mine     |   1.841 ms |   1.549 ms | 0.20 |   6,256,290 | Yes
Dmitry   |   5.237 ms |   4.740 ms | 0.27 |  17,808,335 | Yes
Original |   5.705 ms |   5.178 ms | 0.31 |  19,411,876 | Base
Billy    |   6.027 ms |   5.374 ms | 0.31 |  20,477,533 | Yes
SAkbari3 |  39.369 ms |  36.608 ms | 2.27 | 134,030,971 | Yes
SAkbari1 |  46.502 ms |  44.410 ms | 1.67 | 158,181,468 | Yes
SAkbari2 |  74.398 ms |  72.187 ms | 1.41 | 253,311,101 | Yes
Heinz    | 259.090 ms | 254.766 ms | 2.62 | 881,738,225 | Yes

摘要

正则表达糟透了!

答案 3 :(得分:3)

一些建议:

  • 在您知道字符串包含至少一个重复字符之后,才能创建StringBuilder
  • 要避免在StringBuilder内调整大小,请使用获取初始容量的构造函数,并将原始长度减去1。
  • 不要调用ToCharArray(),因为它会分配一个新数组并将字符串复制到其中。

    public static string RemoveRepeatedChars(string s)
    {
        if ((s == null) || (s.Length < 2))
            return s;
    
        // Return original string if no repeated char
        int i = 1;
        while ((i < s.Length) && (s[i] != s[i - 1]))
            i++;
        if (i == s.Length)
            return s;
    
        // i is index of first repeat
        var sb = new StringBuilder(s.Length - 1);
        sb.Append(s, 0, i); // add everything before the first repeat
        char prevChar = s[i];
        i++; // skip the first repeat
        for (; i < s.Length; i++)
        {
            if (s[i] != prevChar)
            {
                sb.Append(s[i]);
                prevChar = s[i];
            }
        }
        return sb.ToString();
    }
    

答案 4 :(得分:2)

这是一个LINQ解决方案:

string s = "reeeturrrnneeddryyf";
var result = string.Join("", s.Where((x, index) => index == s.Length - 1
                             || x != s[index + 1]).ToArray());

或另一种方式:

var result = string.Join("", s.Zip(s.Skip(1), (first, second) => new[] { first, second })
                     .Where(z => z[0] != z[1]).Select(c => c[0])
                     .Concat(new[] { s[s.Length - 1] }));

或另一种方式:(使用扩展方法)

public static class Extensions
{
    public static IEnumerable<T> TrimmingDuplicateCharacters<T>(this IEnumerable<T> source)
    {
        using (var iterator = source.GetEnumerator())
        {
            var comparer = EqualityComparer<T>.Default;

            if (!iterator.MoveNext())
                yield break;

            var current = iterator.Current;
            yield return current;

            while (iterator.MoveNext())
            {
                if (comparer.Equals(iterator.Current, current))
                    continue;

                current = iterator.Current;
                yield return current;
            }
        }
    }
}

然后:

var result = string.Join("", s.TrimmingDuplicateCharacters());

答案 5 :(得分:0)

//Program using C Language
#include <stdio.h>
int main()
{
    char a[] = "rrrrreetttuurrneed";
    int i = 0;
    while(a[i])
    {
        if(a[i] != a[i+1])
        printf("%c", a[i]);
        i++;
    }
    printf("\n");
    return 0;
}