我最近在一个编码面试测试中遇到了一个问题。问题如下。
假设有一项向用户发送消息的服务。每个消息的最大长度为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
的值而变得很复杂。有没有更简单的方法来解决问题?
答案 0 :(得分:0)
感谢发布此帖子,我确实很喜欢这类问题。
就像上面提到的domen一样,这里存在一个挑战,因为您不知道需要多少行。因此,您不知道消息号/消息总数是否允许2(或更多)位。另外,您是否可以使用十六进制(16条消息需要一个数字,或者甚至是基数62的格式编号(0-9,然后是A-Z,然后是a-z)?
您当然可以猜测,如果输入超过200个字符,则可以使用两位数的消息号,但是如果消息是单个字母,然后重复单个空格100次,那么您也许可以摆脱一位数的消息号。
因此,您可能会发现需要多次运行该算法。我将假定此问题可以使用一个数字的消息号,如果愿意,您可以增强我的解决方案以使用基数为52的消息号。
我的方法使用2个类:
这是主要的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个字母的单词组成的输入用空格隔开是没有解决方案的。