【问题标题】:Find the minimum number of messages that need to be sent查找需要发送的最小消息数
【发布时间】:2019-05-03 22:47:45
【问题描述】:

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

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

例如-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 的值。有没有更简单的方法来解决这个问题?

【问题讨论】:

  • 好吧,如果不浏览整个消息,您将无法知道n,因为您无法知道它是如何构造的(15 个字符的消息是一个极端),因此您需要将其“拆分”为带有n(例如0)占位符的消息,获取n,使用真正的n再次传递(如果> 9,消息边界可能会改变),重复直到n保持不变。对我来说,您如何以不同的方式做到这一点并不明显,很想知道是否存在这种方式。

标签: string algorithm optimization greedy


【解决方案1】:

感谢您发布此内容,我确实喜欢这些问题。

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

您当然可以猜测,如果输入超过 200 个字符,那么您可能会使用两位数的消息编号,但如果消息是单个字母后跟一个重复的空格100 次,那么您可能会得到个位数的消息号码。

因此,您可能会发现需要多次运行该算法。对于这个问题,我假设单个数字的消息号是可以接受的,如果你愿意,你可以增强我的解决方案以使用 base 52 消息号。

我的方法使用 2 个类:

  1. 创建一个表示单行消息的 MessageLine 类。
  2. MessageSender 一个收集 MessageLine(s) 的类。它有一个辅助方法来处理消息并返回 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);
            }
        }
    }
}

这里是 Message Line 类:

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();
    }
}

我测试了你的场景,它似乎产生了正确的结果。

如果我们得到这份工作,请告诉我。 :-)

【讨论】:

  • 如果这些答案中的任何一个对您有所帮助,您能否接受其中一个作为答案并“投赞成票”。只需点击您认为对您最有帮助的答案旁边的灰色小“复选标记”即可。
【解决方案2】:

您可以对子消息的总数进行二进制搜索。即从两个数 L 和 H 开始,这样你就知道 L 个子消息是不够的,而 H 个子消息是足够的,通过尝试在假设涉及许多子消息:如果是,则将其设为新 H,否则将其设为新 L。一旦 H = L+1 停止:H 是有效的子消息的最小数量,因此构造一个使用这么多子消息的实际解决方案。这将需要 O(n log n) 时间。

要获得 L 和 H 的初始值,您可以从 1 开始并不断加倍,直到获得足够高的数字。第一个大到可以工作的值是你的 H,前一个是你的 L。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-23
    • 2021-03-29
    • 2015-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多