找到需要发送的最少邮件数量

时间:2019-05-02 18:47:10

标签: string algorithm optimization greedy

我最近在一个编码面试测试中遇到了一个问题。问题如下。

  

假设有一项向用户发送消息的服务。每个消息的最大长度为30个字符。该服务会收到一条完整的消息,然后将其分为多个子消息,每个子消息的大小最多为30个字符。但是服务存在问题。它不能保证用户接收子消息的顺序。因此,对于每个子消息,它都附加一个后缀(k / n),其中k表示n个子消息中的第k个子消息。计算子消息中的字符数不能超过30个时,也要考虑此后缀。找到发送所需的最小子消息数。

Eg-1:

消息:敏捷的棕色狐狸跳过了懒狗

第一个子消息可以是:褐狐快闪跳(1/2)但是

以上内容不正确,因为超过30个字符。它有31个字符。

因此,正确的子消息是:

褐狐狸(1/2)

跳过懒狗(2/2)

答案是2。

Eg-2:

消息:敏捷的棕色狐狸跳过了懒惰的乌龟

因此,正确的子消息是:

褐狐狸(1/3)

跳过懒惰(2/3)

乌龟(3/3)

所以,答案是3。

Eg-3:

消息:你好,我叫

子消息:你好,我叫

答案= 1。

注意:一个单词不能在子消息中打断。假设单词长度不超过30个字符。如果仅显示一条消息,则无需使用后缀

我的方法:如果字符串的总字符长度小于30,则返回1。否则返回子消息,直到字符数为30,然后对每个单词进行检查。但是现在由于我不知道后缀n的值而变得很复杂。有没有更简单的方法来解决问题?

2 个答案:

答案 0 :(得分:0)

感谢发布此帖子,我确实很喜欢这类问题。

就像上面提到的domen一样,这里存在一个挑战,因为您不知道需要多少行。因此,您不知道消息号/消息总数是否允许2(或更多)位。另外,您是否可以使用十六进制(16条消息需要一个数字,或者甚至是基数62的格式编号(0-9,然后是A-Z,然后是a-z)?

您当然可以猜测,如果输入超过200个字符,则可以使用两位数的消息号,但是如果消息是单个字母,然后重复单个空格100次,那么您也许可以摆脱一位数的消息号。

因此,您可能会发现需要多次运行该算法。我将假定此问题可以使用一个数字的消息号,如果愿意,您可以增强我的解决方案以使用基数为52的消息号。

我的方法使用2个类:

  1. 创建一个代表消息单行的MessageLine类。
  2. MessageSender一个收集MessageLine的类。它具有一个辅助方法,该方法可以处理消息并返回MessageLines列表。

这是主要的MessageSender类。如果运行它,则可以在命令行上传递一条消息以供处理。

package com.gtajb.stackoverflow;

import java.util.LinkedList;

public class MessageSender {

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Please supply a message to send");
            System.exit(1);
        }

        // Collect the command line parameters into a single string.
        StringBuilder sb = new StringBuilder();
        boolean firstWord = true;
        for (String s: args) {
            if (!firstWord) {
                sb.append(" ");
            }
            firstWord = false;
            sb.append(s);
        }

        // Process the input String and create the MessageSender object.
        MessageSender ms = new MessageSender(sb.toString());
        System.out.println("Input message: " + sb.toString());

        // Retrieve the blocked message and output it.
        LinkedList<MessageLine> msg = ms.getBlockedMessage();
        int lineNo = 0;
        for (MessageLine ml : msg) {
            lineNo += 1;
            System.out.printf("%2d: %s\n", lineNo, ml.getFormattedLine(msg.size()));
        }
    }

    private String msg;

    public MessageSender(String msg) {
        this.msg = msg;
        processMessage();
    }

    private LinkedList<MessageLine> blockedMessage = new LinkedList<MessageLine> ();

    public LinkedList<MessageLine> getBlockedMessage() {
        return blockedMessage;
    }

    private static final int LINE_MAX_SIZE = 30;
    /**
     * A private helper method that processes the supplied message when
     * the object is constructed.
     */
    private void processMessage() {

        // Split the message into words and work out how long the message is.
        String [] words = msg.split("\\s+");
        int messageLength = 0;
        for (String w: words) {
            messageLength += w.length();
        }
        messageLength += words.length - 1;            // Add in the number of words minus one to allow for the single spaces.

        // Can we get away with a single MessageLine?
        if (messageLength < LINE_MAX_SIZE) {
            // A single message line is good enough.
            MessageLine ml = new MessageLine(1);
            blockedMessage.add(ml);
            for (String w: words) {
                ml.add(w);
            }
        } else {
            // Multiple MessageLines will be required.
            int lineNo = 1;
            MessageLine ml = new MessageLine(lineNo);
            blockedMessage.add(ml);
            for (String w: words) {
                    // check if this word will blow the max line length.
                    // The maximum number of lines is 2. It can be anything that is > 1.
                if (ml.getFormattedLineLength(2) + w.length() + 1 > LINE_MAX_SIZE) {
                    // The word will blow the line length, so create a new line.
                    lineNo += 1;
                    ml = new MessageLine(lineNo);
                    blockedMessage.add(ml);
                }
                ml.add(w);
            }
        }
    }
}

这是消息行类:

package com.gtajb.stackoverflow;

import java.util.LinkedList;

public class MessageLine extends LinkedList<String> {

    private int lineNo;
    public MessageLine(int lineNo) {
        this.lineNo = lineNo;
    }

    /**
     * Add a new word to this message line.
     * @param word the word to add
     * @return true if the collection is modified.
     */
    public boolean add(String word) {
        if (word == null || word.trim().length() == 0) {
            return false;
        }
        return super.add(word.trim());
    }

    /**
     * Return the formatted message length.
     * @param totalNumLines the total number of lines in the message.
     * @return the length of this line when formatted.
     */
    public int getFormattedLineLength(int totalNumLines) {
        return getFormattedLine(totalNumLines).length();
    }

    /**
     * Return the formatted line optionally with the line count information.
     * @param totalNumLines the total number of lines in the message.
     * @return the formatted line.
     */
    public String getFormattedLine(int totalNumLines) {

        boolean firstWord = true;
        StringBuilder sb = new StringBuilder();
        for (String w : this) {
            if (! firstWord) {
                sb.append (" ");
            }
            firstWord = false;
            sb.append(w);
        }
        if (totalNumLines > 1) {
            sb.append (String.format(" (%d/%d)", lineNo, totalNumLines));
        }
        return sb.toString();
    }
}

我测试了您的方案,它似乎产生了正确的结果。

让我知道是否我们得到这份工作。 :-)

答案 1 :(得分:0)

您可以对子消息的总数进行二进制搜索。也就是说,以两个数字L和H开头,这样您就知道L个子消息是不够的,并且H个子消息就足够了,并通过尝试在以下条件下构造一个解来查看它们的平均值(L + H)/ 2是否足够假设涉及许多子消息:如果是,则使新的H,否则使它成为新的L。在H = L + 1时立即停止:H是工作的最小子消息数,因此构造一个使用这么多子消息的实际解决方案。这将需要O(n log n)时间。

要获取L和H的初始值,您可以从1开始并保持一倍,直到获得足够高的数字为止。第一个足够大的值可以用作您的H,而第一个值则可以用作L。

顺便说一句,您给出的约束还不足以确保存在解决方案:例如,由两个29个字母的单词组成的输入用空格隔开是没有解决方案的。