【问题标题】:Logic to decrease character values减少字符值的逻辑
【发布时间】:2012-07-04 09:42:33
【问题描述】:

我正在研究减少 字母数字 List<char> 的值的逻辑。比如A10变成A9BBA变成BAZ123变成122。是的,如果输入的值是最后一个值(如 A0),那么我应该返回 -

额外的开销是有一个由用户维护的List<char> 变量。它有要跳过的字符。例如,如果列表中包含 A,则值 GHB 应变为 GGZ 而不是 GHA

这个逻辑的基础是减少char 的一个非常简单的用法,但是在这些条件下,我发现它非常困难。

我的项目是 Silverlight,语言是 C#。以下是我在 3 种方法中尝试执行的代码:

    List<char> lstGetDecrName(List<char> lstVal)//entry point of the value that returns decreased value
    {
        List<char> lstTmp = lstVal;
        subCheckEmpty(ref lstTmp);
        switch (lstTmp.Count)
        {
            case 0:
                lstTmp.Add('-');
                return lstTmp;
            case 1:
                if (lstTmp[0] == '-')
                {
                    return lstTmp;
                }
                break;
            case 2:
                if (lstTmp[1] == '0')
                {
                    if (lstTmp[0] == '1')
                    {
                        lstTmp.Clear();
                        lstTmp.Add('9');
                        return lstTmp;
                    }
                    if (lstTmp[0] == 'A')
                    {
                        lstTmp.Clear();
                        lstTmp.Add('-');
                        return lstTmp;
                    }
                }
                if (lstTmp[1] == 'A')
                {
                    if (lstTmp[0] == 'A')
                    {
                        lstTmp.Clear();
                        lstTmp.Add('Z');
                        return lstTmp;
                    }
                }
                break;
        }
        return lstGetDecrValue(lstTmp,lstVal);
    }



    List<char> lstGetDecrValue(List<char> lstTmp,List<char> lstVal)
    {
        List<char> lstValue = new List<char>();
        switch (lstTmp.Last())
        {
            case 'A':
                lstValue = lstGetDecrTemp('Z', lstTmp, lstVal);
                break;
            case 'a':
                lstValue = lstGetDecrTemp('z', lstTmp, lstVal);
                break;
            case '0':
                lstValue = lstGetDecrTemp('9', lstTmp, lstVal);
                break;
            default:
                char tmp = (char)(lstTmp.Last() - 1);
                lstTmp.RemoveAt(lstTmp.Count - 1);
                lstTmp.Add(tmp);
                lstValue = lstTmp;
                break;
        }
        return lstValue;
    }






    List<char> lstGetDecrTemp(char chrTemp, List<char> lstTmp, List<char> lstVal)//shifting places eg unit to ten,etc.
    {
        if (lstTmp.Count == 1)
        {
            lstTmp.Clear();
            lstTmp.Add('-');
            return lstTmp;
        }
        lstTmp.RemoveAt(lstTmp.Count - 1);
        lstVal = lstGetDecrName(lstTmp);
        lstVal.Insert(lstVal.Count, chrTemp);
        return lstVal;
    }

我非常需要这方面的帮助。请帮我解决这个问题。

【问题讨论】:

  • +1:非常有趣的问题。在下面发布了一个有效的解决方案。提到了我的排除代码中有一个错误,但这应该很容易修复。

标签: c# silverlight logic


【解决方案1】:

您要解决的问题实际上是如何减少字符序列的离散部分,每个部分都有自己的计数系统,其中每个部分由 Alpha 和 Numeric 之间的变化分隔。一旦确定了这一点,剩下的问题就很容易了。

如果您在结果中得到不需要的字符,跳过不需要的字符只是重复递减的问题。

一个困难是序列的模糊定义。例如当你开始说 A00 时该怎么办,下一步是什么? “A”或“-”。为了论证,我假设一个基于 Excel 单元格名称的实际实现(即每个部分独立于其他部分运行)。

下面的代码完成了您想要的 95%,但是排除代码中有一个错误。例如“ABB”变成“AAY”。我觉得需要在更高级别应用排除项(例如,重复递减直到排除列表中没有字符),但我现在没有时间完成它。此外,当它倒计时到零时,它会导致一个空白字符串,而不是你想要的“-”,但这在过程结束时添加是微不足道的。

第 1 部分(将问题分成多个部分):

public static string DecreaseName( string name, string exclusions )
{
    if (string.IsNullOrEmpty(name))
    {
        return name;
    }

    // Split the problem into sections (reverse order)
    List<StringBuilder> sections = new List<StringBuilder>();
    StringBuilder result = new StringBuilder(name.Length);
    bool isNumeric = char.IsNumber(name[0]);
    StringBuilder sb = new StringBuilder();
    sections.Add(sb);
    foreach (char c in name)
    {
        // If we change between alpha and number, start new string.
        if (char.IsNumber(c) != isNumeric)
        {
            isNumeric = char.IsNumber(c);
            sb = new StringBuilder();
            sections.Insert(0, sb);
        }
        sb.Append(c);
    }

    // Now process each section
    bool cascadeToNext = true;
    foreach (StringBuilder section in sections)
    {
        if (cascadeToNext)
        {
            result.Insert(0, DecrementString(section, exclusions, out cascadeToNext));
        }
        else
        {
            result.Insert(0, section);
        }
    }

    return result.ToString().Replace(" ", "");
}

Part2(递减给定字符串):

private static string DecrementString(StringBuilder section, string exclusions, out bool cascadeToNext)
{
    bool exclusionsExist = false;
    do
    {
        exclusionsExist = false;
        cascadeToNext = true;
        // Process characters in reverse
        for (int i = section.Length - 1; i >= 0 && cascadeToNext; i--)
        {
            char c = section[i];
            switch (c)
            {
                case 'A':
                    c = (i > 0) ? 'Z' : ' ';
                    cascadeToNext = (i > 0);
                    break;
                case 'a':
                    c = (i > 0) ? 'z' : ' ';
                    cascadeToNext = (i > 0);
                    break;
                case '0':
                    c = (i > 0) ? '9' : ' ';
                    cascadeToNext = (i > 0);
                    break;
                case ' ':
                    cascadeToNext = false;
                    break;
                default:
                    c = (char)(((int)c) - 1);
                    if (i == 0 && c == '0')
                    {
                        c = ' ';
                    }
                    cascadeToNext = false;
                    break;
            }
            section[i] = c;
            if (exclusions.Contains(c.ToString()))
            {
                exclusionsExist = true;
            }
        }
    } while (exclusionsExist);
    return section.ToString();
}

除法当然可以更有效地完成,只需将开始和结束索引传递给 DecrementString,但这更容易编写和遵循,并且实际上不会慢很多。

【讨论】:

  • 我不喜欢你的字典。它在语义上滥用KeyValuePair,需要将strings 转换为chars,使您容易出现拼写错误('Z' =&gt; "X"'0' =&gt; ""?),需要更多的 LOC 而不是简单的方法来获得所需的charbool(即使是您给出的截断形式),甚至可能不如这种方法性能。
  • @Rawling:代码当时正在进行中,因此出现了拼写错误。已切换到纯代码方法。
  • @HiTechMagic,我整理了一些复杂的代码,现在运行良好。但是你的代码看起来很有希望(不像我的那样混乱,我现在已经使用了 5 个方法调用)。因此,我正在尝试实现您提到的代码,并且像 B1 这样的错误删除应该变为 B0 而不是 B。让我们看看,如果一切正常,那么我可能替换我的代码,或者保持原样:)
  • @vaibhav:如果我有机会修复它(我可以在我自己的一个项目中使用它来增加文件名)。我目前正在运行一个 winforms 测试应用程序。
  • @HiTechMagic,查看我发布的代码。这似乎是最合适的,到目前为止没有发现任何错误。
【解决方案2】:

检查它是否是数字,如果是,则对数字进行减法运算,如果是字符串,则将其更改为字符代码,然后将字符代码减1

【讨论】:

  • 这并不像@Johnn 那样容易,我在开始为其编写逻辑时也有同样的感觉。但所要求的复杂性并不那么容易弄清楚。
  • 我已经编写了这样的代码来生成一个包含所有可能单词的列表,只有我的数量增加而不是 nr 减少
  • 这是问题中最可悲的部分,先生……增加不是问题,减少是问题。我已经有了用于增加值的完美工作代码,但是我的 decreased 代码有很多错误
【解决方案3】:

昨天我一直在想这个,所以这里有一个想法。请注意,这只是伪代码,未经测试,但我认为这个想法是有效的并且应该可以工作(进行一些修改)。

重点是直接定义你的“字母表”,并指定其中哪些字符是非法的,应该跳过,然后使用这个字母表中的位置列表或数组来定义你开始的单词。

我现在不能再花时间在这上面了,但是如果你决定使用它并让它发挥作用,请告诉我!

string[] alphabet = {a, b, c, d, e};
string[] illegal = {c, d};


public string ReduceString(string s){
            // Create a list of the alphabet-positions for each letter:
    int[] positionList = s.getCharsAsPosNrsInAlphabet();
    int[] reducedPositionList = ReduceChar(positionList, positionList.length);

    string result = "";
    foreach(int pos in reducedPositionList){
        result += alphabet[pos];
    }

    return result;
}


public string ReduceChar(string[] positionList, posToReduce){
    int reducedCharPosition = ReduceToNextLegalChar(positionList[posToReduce]);
    // put reduced char back in place:
    positionList[posToReduce] = reducedCharPosition; 

    if(reducedCharPosition < 0){
        if(posToReduce <= 0){
            // Reached the end, reduced everything, return empty array!:
            return new string[](); 
        }
        // move to back of alphabet again (ie, like the 9 in "11 - 2 = 09"):
        reducedCharPosition += alphabet.length;     
        // Recur and reduce next position (ie, like the 0 in "11 - 2 = 09"):
        return ReduceChar(positionList, posToReduce-1); 
    }

    return positionList;
}


public int ReduceToNextLegalChar(int pos){
    int nextPos = pos--;
    return (isLegalChar(nextPos) ? nextPos : ReduceToNextLegalChar(nextPos));
}


public boolean IsLegalChar(int pos){
        return (! illegal.contains(alphabet[pos]));
}
enter code here

【讨论】:

  • 感谢您付出的努力。是的,确切地说,您试图以伪形式传达的想法是它应该遵循的实际工作流程。我已经以类似的方式实现了我的代码。 :) 只是我需要让我的代码在消除错误方面更加严格,而且我已经做到了。
【解决方案4】:

无需为您编写所有代码,这里有一个关于如何分解的建议:

char DecrementAlphaNumericChar(char input, out bool hadToWrap)
{
    if (input == 'A')
    {
        hadToWrap = true;
        return 'Z';
    }
    else if (input == '0')
    {
        hadToWrap = true;
        return '9';
    }
    else if ((input > 'A' && input <= 'Z') || (input > '0' && input <= '9'))
    {
        hadToWrap = false;
        return (char)((int)input - 1);
    }
    throw new ArgumentException(
        "Characters must be digits or capital letters",
        "input");
}

char DecrementAvoidingProhibited(
    char input, List<char> prohibited, out bool hadToWrap)
{
    var potential = DecrementAlphaNumericChar(input, out hadToWrap);
    while (prohibited.Contains(potential))
    {
        bool temp;
        potential = DecrementAlphaNumericChar(potential, out temp);
        if (potential == input)
        {
            throw new ArgumentException(
                "A whole class of characters was prohibited",
                "prohibited");
        }
        hadToWrap |= temp;
    }
    return potential;
}

string DecrementString(string input, List<char> prohibited)
{
    char[] chrs = input.ToCharArray();
    for (int i = chrs.Length - 1; i >= 0; i--)
    {
        bool wrapped;
        chrs[i] = DecrementAvoidingProhibited(
                      chrs[i], prohibited, out wrapped);
        if (!wrapped)
            return new string(chrs);
    }
    return "-";
}

这里唯一的问题是它会减少例如A10A09 不是 A9。实际上我自己更喜欢这个,但是编写一个删除多余零的最终通道应该很简单。

为了获得更高的性能,请将 List&lt;char&gt;s 替换为 Hashset&lt;char&gt;s,它们应该允许更快的 Contains 查找。

【讨论】:

  • 很好,建议,我将不得不应用这个并检查你的最后一个指针的解决方法
【解决方案5】:

我通过其他一些变通方法找到了我自己的答案的解决方案。

调用函数:

    MyFunction()
    {
        //stuff I do before
        strValue = lstGetDecrName(strValue.ToList());//decrease value here
        if (strValue.Contains('-'))
        {
            strValue = "-";
        }
        //stuff I do after
    }

总共有 4 个功能。 2 个主函数和 2 个辅助函数。

    List<char> lstGetDecrName(List<char> lstVal)//entry point, returns decreased value
    {
        if (lstVal.Contains('-'))
        {
            return "-".ToList();
        }
        List<char> lstTmp = lstVal;
        subCheckEmpty(ref lstTmp);
        switch (lstTmp.Count)
        {
            case 0:
                lstTmp.Add('-');
                return lstTmp;
            case 1:
                if (lstTmp[0] == '-')
                {
                    return lstTmp;
                }
                break;
            case 2:
                if (lstTmp[1] == '0')
                {
                    if (lstTmp[0] == '1')
                    {
                        lstTmp.Clear();
                        lstTmp.Add('9');
                        return lstTmp;
                    }
                    if (lstTmp[0] == 'A')
                    {
                        lstTmp.Clear();
                        lstTmp.Add('-');
                        return lstTmp;
                    }
                }
                if (lstTmp[1] == 'A')
                {
                    if (lstTmp[0] == 'A')
                    {
                        lstTmp.Clear();
                        lstTmp.Add('Z');
                        return lstTmp;
                    }
                }
                break;
        }

        List<char> lstValue = new List<char>();
        switch (lstTmp.Last())
        {
            case 'A':
                lstValue = lstGetDecrTemp('Z', lstTmp, lstVal);
                break;
            case 'a':
                lstValue = lstGetDecrTemp('z', lstTmp, lstVal);
                break;
            case '0':
                lstValue = lstGetDecrTemp('9', lstTmp, lstVal);
                break;
            default:
                char tmp = (char)(lstTmp.Last() - 1);
                lstTmp.RemoveAt(lstTmp.Count - 1);
                lstTmp.Add(tmp);
                subCheckEmpty(ref lstTmp);
                lstValue = lstTmp;
                break;
        }
        lstGetDecrSkipValue(lstValue);
        return lstValue;

    }


    List<char> lstGetDecrSkipValue(List<char> lstValue)
    {
        bool blnSkip = false;
        foreach (char tmpChar in lstValue)
        {
            if (lstChars.Contains(tmpChar))
            {
                blnSkip = true;
                break;
            }
        }
        if (blnSkip)
        {
            lstValue = lstGetDecrName(lstValue);
        }
        return lstValue;
    }


    void subCheckEmpty(ref List<char> lstTmp)
    {
        bool blnFirst = true;
        int i = -1;
        foreach (char tmpChar in lstTmp)
        {
            if (char.IsDigit(tmpChar) && blnFirst)
            {
                i = tmpChar == '0' ? lstTmp.IndexOf(tmpChar) : -1;
                if (tmpChar == '0')
                {
                    i = lstTmp.IndexOf(tmpChar);
                }
                blnFirst = false;
            }
        }
        if (!blnFirst && i != -1)
        {
            lstTmp.RemoveAt(i);
            subCheckEmpty(ref lstTmp);
        }
    }


    List<char> lstGetDecrTemp(char chrTemp, List<char> lstTmp, List<char> lstVal)//shifting places eg unit to ten,etc.
    {
        if (lstTmp.Count == 1)
        {
            lstTmp.Clear();
            lstTmp.Add('-');
            return lstTmp;
        }
        lstTmp.RemoveAt(lstTmp.Count - 1);
        lstVal = lstGetDecrName(lstTmp);
        lstVal.Insert(lstVal.Count, chrTemp);
        subCheckEmpty(ref lstVal);
        return lstVal;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    • 1970-01-01
    相关资源
    最近更新 更多