简体   繁体   English

在字边界上将字符串分成2个字符串以最小化长度差异的绝佳方法

[英]Elegant way to split string into 2 strings on word boundaries to minimize length difference

I have a working solution right now, but it seems really ugly for something so (seemingly) simple. 我现在有一个可行的解决方案,但是对于这么简单(看起来)简单的事情来说似乎很难看。

I tried just breaking it when adding a word goes over the halfway mark, both splitting before and after adding the word, but depending on the length of the words it's either imbalanced towards the first or second line. 我尝试在添加单词超过中途标记时打破它,添加单词之前和之后均会拆分,但是根据单词的长度,它可能不平衡于第一行或第二行。

Sample inputs that I was initially having trouble with before the convoluted fix: 在复杂的修复之前,我最初遇到的样本输入是:

Input "Macaroni Cheese" and "Cheese Macaroni" 输入"Macaroni Cheese""Cheese Macaroni"

Should output "Macaroni<br/> Cheese" and "Cheese<br/> Macaroni" respectively. 应分别输出"Macaroni<br/> Cheese""Cheese<br/> Macaroni"

But simpler solutions either worked on the first but not the second, or the other way around. 但是,更简单的解决方案要么在第一个上起作用,但在第二个上却不起作用,或者相反。

So here's what I have that works, but I'm wondering if there's a more elegant way to do this. 所以这就是我的工作方式,但是我想知道是否有更优雅的方法可以做到这一点。

public string Get2LineDisplayText(string original)
{
    string[] words = original.Split(new[] {' ', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);

    //Degenerate case with only 1 word
    if (words.Length <= 1)
    {
        return original;
    }

    StringBuilder builder = new StringBuilder();
    builder.Append(words[0]); //Add first word without prepending space
    bool addedBr = false;
    foreach (string word in words.Skip(1))
    {
        if (builder.Length + word.Length < original.Length / 2) //Word fits on the first line without passing halfway mark
        {
            builder.Append(' ' + word);
        }
        else if (!addedBr) //Adding word goes over half, need to see if it's more balanced on the 1st or 2nd line
        {
            int diffOnLine1 = Math.Abs((builder.Length + word.Length) - (original.Length - builder.Length - word.Length));
            int diffOnLine2 = Math.Abs((builder.Length) - (original.Length - builder.Length));
            if (diffOnLine1 < diffOnLine2)
            {
                builder.Append(' ' + word);
                builder.Append("<br/>");
            }
            else
            {
                builder.Append("<br/>");
                builder.Append(' ' + word);
            }
            addedBr = true;
        }
        else //Past halfway and already added linebreak, just append
        {
            builder.Append(' ' + word);
        }
    }

    return builder.ToString();
}

Sample input/output: 样本输入/输出:

Here's what I came up with: 这是我想出的:

    public static string Get2Lines(string input)
    {
        //Degenerate case with only 1 word
        if (input.IndexOf(' ') == -1)
        {
            return input;
        }
        int mid = input.Length / 2;

        int first_index_after = input.Substring(mid).IndexOf(' ') + mid;
        int first_index_before = input.Substring(0, mid).LastIndexOf(' ');

        if (first_index_after - mid < mid - first_index_before)
            return input.Insert(first_index_after, "<BR />");
        else
            return input.Insert(first_index_before, "<BR />");
    }
public static string Get2LineDisplayText(string original)
{
    //Degenerate case with only 1 word
    if (!original.Any(Char.IsWhiteSpace))
    {
        return original;
    }
    int mid = original.Length / 2;
    if (!Char.IsWhiteSpace(original[mid]))
    {
        for (int i = 1; i < mid; i += i)
        {
            if (Char.IsWhiteSpace(original[mid + i]))
            {
                mid = mid + i;
                break;
            }
            if (Char.IsWhiteSpace(original[mid - i]))
            {
                mid = mid - i;
                break;
            }
        }
    }

    return original.Substring(0, mid)
           + "<br />" + original.Substring(mid + 1);
}

I tried my hand at it and arrived at : 我试了一下就到了:

String splitInMiddle(String s) {
    int middle = s.length() / 2;
    int right = s.indexOf(" ",middle);
    int left = s.lastIndexOf(" ",middle);

    int split = right;
    if ((right < 0) || (left + right > 2*middle)) {
        split = left;
    }
    return s.substring(0, split) + "<br/>\n" + s.substring(split + 1);
}

The principle is that it looks for the first space after and the last space before. 原理是,它寻找后的第一个空格和前的最后一个空格。 If the left one is closer than the right one pick that. 如果左边的一个比右边的一个更近,那就选择它。

Then glue the pieces I want with a CR instead of a space. 然后用CR(而不是空格)粘上我想要的碎片。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM