Эффективно изменить порядок слов (не символов) в массиве символов
учитывая массив символов, который формирует предложение слов, дайте эффективный алгоритм для изменения порядка слов (а не символов) в нем.
пример ввода и вывода:
>>> reverse_words("this is a string")
'string a is this'
это должно быть O(N) время и O (1) пространство (split()
и нажатие на / выскакивание стека не допускается).
головоломка взята из здесь.
8 ответов
решение на C / C++:
void swap(char* str, int i, int j){
char t = str[i];
str[i] = str[j];
str[j] = t;
}
void reverse_string(char* str, int length){
for(int i=0; i<length/2; i++){
swap(str, i, length-i-1);
}
}
void reverse_words(char* str){
int l = strlen(str);
//Reverse string
reverse_string(str,strlen(str));
int p=0;
//Find word boundaries and reverse word by word
for(int i=0; i<l; i++){
if(str[i] == ' '){
reverse_string(&str[p], i-p);
p=i+1;
}
}
//Finally reverse the last word.
reverse_string(&str[p], l-p);
}
Это должно быть o(n) по времени и O(1) в пространстве.
Edit: немного очистил его.
первый проход над строкой, очевидно, O(n/2) = O (n). Второй проход-O(N + комбинированная длина всех слов / 2) = O(n + n/2) = O(n), что делает этот алгоритм o (n).
нажатие строки на стек, а затем ее выкидывание-это все еще O (1)? по сути, это то же самое, что и использование split()...
не за O(1) означает? Эта задача становится легкой, если мы можем просто добавлять строки и прочее, но это использует пространство...
редактировать: Томас Уотнедал прав. Следующий алгоритм-O (n) во времени и O (1) в пространстве:
- обратная строка на месте (первая итерация по строке)
- обратный каждой (reversed) word in-place (еще две итерации по строке)
- найти первое слово граница
- обратный внутри этого слова границы
- повторите для следующего слова до конца
Я думаю, нам нужно будет доказать, что Шаг 2 действительно только O(2n)...
#include <string>
#include <boost/next_prior.hpp>
void reverse(std::string& foo) {
using namespace std;
std::reverse(foo.begin(), foo.end());
string::iterator begin = foo.begin();
while (1) {
string::iterator space = find(begin, foo.end(), ' ');
std::reverse(begin, space);
begin = boost::next(space);
if (space == foo.end())
break;
}
}
вот мой ответ. Никаких вызовов библиотек и временных структур данных.
#include <stdio.h>
void reverse(char* string, int length){
int i;
for (i = 0; i < length/2; i++){
string[length - 1 - i] ^= string[i] ;
string[i] ^= string[length - 1 - i];
string[length - 1 - i] ^= string[i];
}
}
int main () {
char string[] = "This is a test string";
char *ptr;
int i = 0;
int word = 0;
ptr = (char *)&string;
printf("%s\n", string);
int length=0;
while (*ptr++){
++length;
}
reverse(string, length);
printf("%s\n", string);
for (i=0;i<length;i++){
if(string[i] == ' '){
reverse(&string[word], i-word);
word = i+1;
}
}
reverse(&string[word], i-word); //for last word
printf("\n%s\n", string);
return 0;
}
@Daren Thomas
реализация вашего алгоритма (O (N) во времени, O (1) в пространстве) В D (цифровой Марс):
#!/usr/bin/dmd -run
/**
* to compile & run:
* $ dmd -run reverse_words.d
* to optimize:
* $ dmd -O -inline -release reverse_words.d
*/
import std.algorithm: reverse;
import std.stdio: writeln;
import std.string: find;
void reverse_words(char[] str) {
// reverse whole string
reverse(str);
// reverse each word
for (auto i = 0; (i = find(str, " ")) != -1; str = str[i + 1..length])
reverse(str[0..i]);
// reverse last word
reverse(str);
}
void main() {
char[] str = cast(char[])("this is a string");
writeln(str);
reverse_words(str);
writeln(str);
}
выход:
this is a string string a is this