2008-09-06 63 views
14

Cho một dãy các ký tự tạo thành một câu, cung cấp thuật toán hiệu quả để đảo ngược thứ tự của các từ (không phải ký tự) trong đó.Đảo ngược thứ tự các từ (không phải ký tự) một cách hiệu quả trong một dãy ký tự

Ví dụ đầu vào và đầu ra:

>>> reverse_words("this is a string") 
'string a is this' 

Nên O (N) thời gian và O (1) không gian (split() và đẩy bật/popping ra khỏi ngăn xếp không được phép).

Câu đố được lấy từ here.

Trả lời

33

Một giải pháp trong 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); 
} 

này cần được O (n) trong thời gian và O (1) trong không gian.

Chỉnh sửa: Làm sạch nó một chút.

Vượt qua đầu tiên qua chuỗi rõ ràng là O (n/2) = O (n). Pass thứ hai là O (n + chiều dài kết hợp của tất cả các từ/2) = O (n + n/2) = O (n), mà làm cho một thuật toán O (n).

+0

Xin chào, tôi đã tò mò nếu độ phức tạp của lần truyền thứ hai là n + tất cả các từ/2, coz cho mỗi lần truyền trong lần thứ hai bạn đang thực hiện tính toán chiều dài/2, do đó nó có chiều dài từ * wordlength/2 + v.v. .... sửa tôi nếu tôi sai .. – sriks

+0

@sriks: Trong trường hợp này bạn không thể nhân sự phức tạp của bên ngoài với công việc bên trong. Bằng cách sử dụng [Amortized_analysis] (http://en.wikipedia.org/wiki/Amortized_analysis) bạn có thể thấy rằng tổng chi phí của toàn bộ hoạt động reverse_words là tuyến tính. Lưu ý rằng các hoạt động reverse_string trong vòng lặp for được thực hiện cho mỗi từ và có độ phức tạp tuyến tính đến độ dài từ. Điều đó sẽ không bao giờ lớn hơn tổng chiều dài chuỗi. –

+0

@ThomasWatnedal: Tôi nghĩ rằng những gì sriks nói có ý nghĩa, cho phép mang nó theo cách này, bạn có một câu hoặc một chuỗi bao gồm k từ mỗi chiều dài x và cách nhau bởi k-1 không gian có nghĩa là tổng chiều dài của câu = k * x + (k-1), bây giờ xem xét cấu trúc vòng lặp lồng nhau trong đó vòng lặp ngoài làm cho một vượt qua toàn bộ chuỗi và vòng lặp bên trong đảo ngược các từ. Trong mỗi lần đảo ngược bạn thực hiện x/2 hoạt động trên mỗi từ, do đó tổng chi phí đảo ngược = k * x/2 và chi phí của thuật toán là khoảng (k * x)^2 là hàm bậc hai. Bạn có thể biện minh cho lý luận của mình không? – AnkitSablok

1

Trong mã giả:

reverse input string 
reverse each word (you will need to find word boundaries) 
0

Đẩy mỗi từ trên một chồng. Bật tất cả các từ khỏi ngăn xếp.

+0

Nó không đáp ứng yêu cầu (.) của O (1) trong không gian (Số từ là O (N)) – jfs

+0

Giữ một chuỗi trong bộ nhớ là O (N) trong không gian đã có. Kể từ khi 2 * O (N) = O (N), tôi không chắc chắn những gì bạn chứng minh. Và ở bất kỳ tỷ lệ nào, bất kỳ thuật toán nào di chuyển một từ tại một thời điểm thay vì một chữ cái tại một thời điểm sẽ có độ phức tạp không gian O (M), trong đó M là từ lớn nhất. Nếu đó là ràng buộc thì câu trả lời là: 1) Đảo ngược mảng ký tự (sử dụng các thủ thuật nổi tiếng để đảo ngược mảng). 2) Tại mỗi ranh giới từ, đảo ngược các ký tự trong phạm vi ranh giới. – Jason

+1

@ Jason: 1.O (1) trong không gian có nghĩa là tại chỗ (cộng với bất kỳ số lượng bộ nhớ bổ sung nếu nó không phát triển với N). 2. bạn có thể đảo ngược một từ tại chỗ quá (như câu trả lời chấp nhận chứng minh) – jfs

1

Trong C: (C99)

#include <stdio.h> 
#include <string.h> 

void reverseString(char* string, int length) 
{ 
    char swap; 
    for (int i = 0; i < length/2; i++) 
    { 
     swap = string[length - 1 - i]; 
     string[length - 1 - i] = string[i]; 
     string[i] = swap; 
    } 
} 

int main (int argc, const char * argv[]) { 
    char teststring[] = "Given an array of characters which form a sentence of words, give an efficient algorithm to reverse the order of the words (not characters) in it."; 
    printf("%s\n", teststring); 
    int length = strlen(teststring); 
    reverseString(teststring, length); 
    int i = 0; 
    while (i < length) 
    { 
     int wordlength = strspn(teststring + i, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); 
     reverseString(teststring + i, wordlength); 
     i += wordlength + 1; 
    } 
    printf("%s\n", teststring); 
    return 0; 
} 

Điều này cho phép đầu ra:

Với một loạt các ký tự mà hình thức một câu từ, đưa ra một thuật toán hiệu quả để đảo ngược thứ tự của các từ (không phải ký tự) trong .

.it in) ký tự không (lời trật tự ngược lại để thuật toán hiệu quả một Hãy cho, lời của câu một hình thức mà nhân vật của mảng một Với

này cần có thời gian ở 4N nhất ., với không gian liên tục nhỏ Thật không may, nó không xử lý dấu chấm câu hoặc trường hợp một cách duyên dáng

+0

s/strspn \\ ([^)] + \\)/strcspn (chuỗi test + i, "\ t \ r \ n \ f")/ – jfs

1

O (N) trong không gian và O (N) trong dung dịch thời gian bằng Python:.

def reverse_words_nosplit(str_): 
    """ 
    >>> f = reverse_words_nosplit 
    >>> f("this is a string") 
    'string a is this' 
    """ 
    iend = len(str_) 
    s = "" 
    while True: 
    ispace = str_.rfind(" ", 0, iend) 
    if ispace == -1: 
     s += str_[:iend] 
     break 
    s += str_[ispace+1:iend] 
    s += " " 
    iend = ispace 
    return s 
1

Bạn sẽ sử dụng hàm được gọi là hàm đệ quy lặp lại, là O (N) theo thời gian khi cần N (N là số từ) lặp và O (1) trong không gian vì mỗi lần lặp trạng thái riêng của nó trong các đối số hàm.

(define (reverse sentence-to-reverse) 
    (reverse-iter (sentence-to-reverse "")) 

(define (reverse-iter(sentence, reverse-sentence) 
    (if (= 0 string-length sentence) 
    reverse-sentence 
    (reverse-iter(remove-first-word(sentence), add-first-word(sentence, reverse-sentence))) 

Lưu ý: Tôi đã viết đề án này là một người mới hoàn chỉnh, vì vậy xin lỗi vì thiếu thao tác chuỗi chính xác.

remove-đầu-word thấy ranh giới từ đầu tiên của câu, sau đó mất rằng phần của nhân vật (bao gồm cả không gian và dấu chấm câu) và loại bỏ nó và trả về câu mới

add-đầu-word thấy từ đầu tiên ranh giới của câu, sau đó lấy phần ký tự đó (bao gồm dấu cách và dấu chấm câu) và thêm nó vào câu ngược và trả về nội dung câu ngược mới.

0

Một C++ giải pháp:

#include <string> 
#include <iostream> 
using namespace std; 

string revwords(string in) { 
    string rev; 
    int wordlen = 0; 
    for (int i = in.length(); i >= 0; --i) { 
     if (i == 0 || iswspace(in[i-1])) { 
      if (wordlen) { 
       for (int j = i; wordlen--;) 
        rev.push_back(in[j++]); 
       wordlen = 0; 
      } 
      if (i > 0) 
       rev.push_back(in[i-1]); 
     } 
     else 
      ++wordlen; 
    } 
    return rev; 
} 

int main() { 
    cout << revwords("this is a sentence") << "." << endl; 
    cout << revwords(" a sentence with extra spaces ") << "." << endl; 
    return 0; 
} 
+0

Giải pháp của bạn là O (N) trong không gian thay vì yêu cầu O (1). – jfs

5

đẩy một chuỗi lên một chồng và sau đó popping nó đi - đó là vẫn còn O (1)? về cơ bản, điều đó giống như sử dụng split() ...

Không O (1) có nghĩa là tại chỗ? Công việc này trở nên dễ dàng nếu chúng ta chỉ có thể nối chuỗi và nội dung, nhưng sử dụng không gian ...

EDIT: Thomas Watnedal là đúng. Thuật toán sau đây là O (n) trong thời gian và O (1) trong không gian:

  1. chuỗi ngược tại chỗ (lặp đầu tiên trên string)
  2. đảo ngược từng từ (đảo ngược) tại chỗ (hai khác lặp đi lặp lại qua chuỗi)
    1. tìm ranh giới từ đầu tiên
    2. ngược bên trong ranh giới từ này
    3. lặp lại cho từ kế tiếp cho đến khi xong

Tôi đoán chúng ta sẽ cần phải chứng minh rằng bước 2 thực sự là chỉ O (2n) ...

+0

O (1) trong không gian có nghĩa là tại chỗ (cộng với bất kỳ số lượng bộ nhớ bổ sung nếu nó không phát triển với N). – jfs

0
using System; 

namespace q47407 
{ 
    class MainClass 
    { 
     public static void Main(string[] args) 
     { 
      string s = Console.ReadLine(); 
      string[] r = s.Split(' '); 
      for(int i = r.Length-1 ; i >= 0; i--) 
       Console.Write(r[i] + " "); 
      Console.WriteLine(); 

     } 
    } 
} 

chỉnh sửa: tôi đoán tôi nên đọc toàn bộ câu hỏi ... mang về.

+0

split() không được phép. – jfs

3
#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; 
    } 
} 
+0

Điều này * là * đúng hướng, nhưng sử dụng đệ quy như vậy phá vỡ giới hạn O (1): Ngăn xếp cuộc gọi sẽ chỉ tiếp tục phát triển tương ứng với số từ trong đầu vào. Ngoài ra, bạn sử dụng ngược lại (foo.begin(), foo.end()) trước vòng lặp while - đây là ** BUG **, vì không có cách nào chấm dứt! –

+1

Rất tiếc (liên quan đến nhận xét ở trên): Tôi không nhận thấy rằng Leon đang sử dụng hai hàm reverse() khác nhau, và do đó không sử dụng đệ quy nào cả. Lời xin lỗi của tôi! Leon, bạn có thể vui lòng sửa bài viết của bạn và thêm một std :: qualifier để đảo ngược (foo.begin(), fo.end())? –

+1

Thêm std :: qualifier để tránh sự nhầm lẫn –

1

@Daren Thomas

Thực hiện các thuật toán của bạn (O (N) trong thời gian, O (1) trong không gian) trong D (Digital Mars):

#!/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); 
} 

Output:

this is a string 
string a is this
1

trong Ruby

"đây là chuỗi" .split.reverse.tham gia (" ")

+0

split() không được phép (yêu cầu O (N) trong không gian) – jfs

0

trong C#, tại chỗ, O (n), và thử nghiệm:

static char[] ReverseAllWords(char[] in_text) 
{ 
    int lindex = 0; 
    int rindex = in_text.Length - 1; 
    if (rindex > 1) 
    { 
     //reverse complete phrase 
     in_text = ReverseString(in_text, 0, rindex); 

     //reverse each word in resultant reversed phrase 
     for (rindex = 0; rindex <= in_text.Length; rindex++) 
     { 
      if (rindex == in_text.Length || in_text[rindex] == ' ') 
      { 
       in_text = ReverseString(in_text, lindex, rindex - 1); 
       lindex = rindex + 1; 
      } 
     } 
    } 
    return in_text; 
} 

static char[] ReverseString(char[] intext, int lindex, int rindex) 
{ 
    char tempc; 
    while (lindex < rindex) 
    { 
     tempc = intext[lindex]; 
     intext[lindex++] = intext[rindex]; 
     intext[rindex--] = tempc; 
    } 
    return intext; 
} 
0

hiệu quả về mặt thời gian của tôi: mất dưới 2 phút để viết trong REBOL :

reverse_words: func [s [string!]] [form reverse parse s none] 

Hãy thử nó ra: reverse_words "này là một chuỗi" "chuỗi a là này"

0

Giải pháp Ruby.

# Reverse all words in string 
def reverse_words(string) 
    return string if string == '' 

    reverse(string, 0, string.size - 1) 

    bounds = next_word_bounds(string, 0) 

    while bounds.all? { |b| b < string.size } 
    reverse(string, bounds[:from], bounds[:to]) 
    bounds = next_word_bounds(string, bounds[:to] + 1) 
    end 

    string 
end 

# Reverse a single word between indices "from" and "to" in "string" 
def reverse(s, from, to) 
    half = (from - to)/2 + 1 

    half.times do |i| 
     s[from], s[to] = s[to], s[from] 
     from, to = from.next, to.next 
    end 

    s 
end 

# Find the boundaries of the next word starting at index "from" 
def next_word_bounds(s, from) 
    from = s.index(/\S/, from) || s.size 
    to = s.index(/\s/, from + 1) || s.size 

    return { from: from, to: to - 1 } 
end 
2

Đây là câu trả lời của tôi. Không có cuộc gọi thư viện và không có cấu trúc dữ liệu tạm thời.

#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; 
} 
+1

'length = sizeof (string) -1;' có thể được sử dụng ở đây. Bạn có thể trích xuất các hàm 'mystrlen()', 'xor_swap()' để dễ đọc. – jfs

0

Vấn đề này có thể được giải quyết bằng O (n) theo thời gian và O (1) trong không gian. Mẫu mã trông như đã đề cập dưới đây:

public static string reverseWords(String s) 
    { 

     char[] stringChar = s.ToCharArray(); 
     int length = stringChar.Length, tempIndex = 0; 

     Swap(stringChar, 0, length - 1); 

     for (int i = 0; i < length; i++) 
     { 
      if (i == length-1) 
      { 
       Swap(stringChar, tempIndex, i); 
       tempIndex = i + 1; 
      } 
      else if (stringChar[i] == ' ') 
      { 
       Swap(stringChar, tempIndex, i-1); 
       tempIndex = i + 1; 
      } 
     } 

     return new String(stringChar); 
    } 

    private static void Swap(char[] p, int startIndex, int endIndex) 
    { 
     while (startIndex < endIndex) 
     { 
      p[startIndex] ^= p[endIndex]; 
      p[endIndex] ^= p[startIndex]; 
      p[startIndex] ^= p[endIndex]; 
      startIndex++; 
      endIndex--; 
     } 
    } 
0

Một lót:

l="Is this as expected ??" 
" ".join(each[::-1] for each in l[::-1].split()) 

Output:

'?? expected as this Is' 
0

Thuật toán: 1) .Reverse mỗi từ của chuỗi. 2) .Reverse resultant String.

public class Solution { 
public String reverseWords(String p) { 
    String reg=" "; 
    if(p==null||p.length()==0||p.equals("")) 
{ 
    return ""; 
} 
String[] a=p.split("\\s+"); 
StringBuilder res=new StringBuilder();; 
for(int i=0;i<a.length;i++) 
{ 

    String temp=doReverseString(a[i]); 
    res.append(temp); 
    res.append(" "); 
} 
String resultant=doReverseString(res.toString()); 
System.out.println(res); 
return resultant.toString().replaceAll("^\\s+|\\s+$", ""); 
} 


public String doReverseString(String s)`{` 


char str[]=s.toCharArray(); 
int start=0,end=s.length()-1; 
while(start<end) 
{ 
char temp=str[start]; 
str[start]=str[end]; 
str[end]=temp; 
start++; 
end--; 
} 
String a=new String(str); 
return a; 

} 

public static void main(String[] args) 
{ 
Solution r=new Solution(); 
String main=r.reverseWords("kya hua"); 
//System.out.println(re); 
System.out.println(main); 
} 
} 
0

Thuật toán để giải quyết vấn đề này được dựa trên quá trình hai bước, bước đầu tiên sẽ đảo ngược những từ riêng lẻ của chuỗi, sau đó trong bước thứ hai, ngược lại toàn bộ chuỗi. Việc thực hiện thuật toán sẽ mất thời gian O (n) và độ phức tạp không gian O (1).

 #include <stdio.h> 
     #include <string.h> 

     void reverseStr(char* s, int start, int end); 

     int main() 
     { 
       char s[] = "This is test string"; 

       int start = 0; 
       int end = 0; 
       int i = 0; 

       while (1) { 

       if (s[i] == ' ' || s[i] == '\0') 
       { 
        reverseStr(s, start, end-1); 
        start = i + 1; 
        end = start; 
       } 
       else{ 
        end++; 
       } 

       if(s[i] == '\0'){ 
        break; 
       } 
       i++; 
     } 

     reverseStr(s, 0, strlen(s)-1); 
     printf("\n\noutput= %s\n\n", s); 

     return 0; 
    } 

    void reverseStr(char* s, int start, int end) 
    { 
    char temp; 
    int j = end; 
    int i = start; 

    for (i = start; i < j ; i++, j--) { 
      temp = s[i]; 
      s[i] = s[j]; 
      s[j] = temp; 
    } 
} 
1

chương trình này là để đảo ngược bản án sử dụng con trỏ IN "ngôn ngữ C" Bằng Vasantha kumar & Sundaramoorthy từ Kongu ENGG COLLEGE, Erode.

LƯU Ý: Câu phải kết thúc với dot vì nhân vật NULL không được gán tự động ở phần cuối của câu *

#include<stdio.h> 
#include<string.h> 

int main() 
{ 
char *p,*s="this is good.",*t; 
int i,j,a,l,count=0; 

l=strlen(s); 

p=&s[l-1]; 

t=&s[-1]; 
while(*t) 
    { 
     if(*t==' ') 
    count++; 
    t++; 
    } 
    a=count; 
    while(l!=0) 
    { 
for(i=0;*p!=' '&&t!=p;p--,i++); 
    p++; 

    for(;((*p)!='.')&&(*p!=' ');p++) 
    printf("%c",*p); 
    printf(" "); 
    if(a==count) 
    { 
    p=p-i-1; 
    l=l-i; 
    } 
    else 
    { 
    p=p-i-2; 
    l=l-i-1; 
    } 

count--; 
    } 

return 0; 
} 
Các vấn đề liên quan