Làm thế nào chúng ta có thể đảo ngược một chuỗi đơn giản trong cờ vây?
Làm thế nào chúng ta có thể đảo ngược một chuỗi đơn giản trong cờ vây?
Câu trả lời:
Trong Go1 rune là một loại nội trang.
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
Russ Cox, trong danh sách gửi thư golang-nut , gợi ý
package main
import "fmt"
func main() {
input := "The quick brown 狐 jumped over the lazy 犬"
// Get Unicode code points.
n := 0
rune := make([]rune, len(input))
for _, r := range input {
rune[n] = r
n++
}
rune = rune[0:n]
// Reverse
for i := 0; i < n/2; i++ {
rune[i], rune[n-1-i] = rune[n-1-i], rune[i]
}
// Convert back to UTF-8.
output := string(rune)
fmt.Println(output)
}
rune:=[]rune(input)
?
Điều này hoạt động mà không cần tất cả các chức năng:
func Reverse(s string) (result string) {
for _,v := range s {
result = string(v) + result
}
return
}
Điều này hoạt động trên chuỗi unicode bằng cách xem xét 2 điều:
Vì vậy, ở đây nó đi:
func reverse(s string) string {
o := make([]int, utf8.RuneCountInString(s));
i := len(o);
for _, c := range s {
i--;
o[i] = c;
}
return string(o);
}
i:=len(o)-1
và sau đó gấp for thành một dòng for _, c:=range s { o[i--]=c; }
. Man I HATE the for mà không có dấu ngoặc đơn - điều này có được phép không:for(_, c:=range s) { o[i--]=c; }
Dự án ví dụ từ Go: golang / example / stringutil / reverse.go , bởi Andrew Gerrand
/*
Copyright 2014 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
Go Playground để đảo ngược một chuỗi
Sau khi đảo ngược chuỗi "bròwn", kết quả đúng phải là "nwòrb", không phải "nẁorb".
Lưu ý phần mộ phía trên chữ o.
Để bảo toàn Unicode kết hợp các ký tự như "as⃝df̅" với kết quả ngược lại "f̅ds⃝a",
vui lòng tham khảo một mã khác được liệt kê bên dưới:
Tôi nhận thấy câu hỏi này khi Simon đăng giải pháp của anh ấy , vì các chuỗi là bất biến, rất kém hiệu quả. Các giải pháp đề xuất khác cũng còn nhiều thiếu sót; chúng không hoạt động hoặc chúng không hiệu quả.
Đây là một giải pháp hiệu quả hoạt động, ngoại trừ khi chuỗi không phải là UTF-8 hợp lệ hoặc chuỗi chứa các ký tự kết hợp.
package main
import "fmt"
func Reverse(s string) string {
n := len(s)
runes := make([]rune, n)
for _, rune := range s {
n--
runes[n] = rune
}
return string(runes[n:])
}
func main() {
fmt.Println(Reverse(Reverse("Hello, 世界")))
fmt.Println(Reverse(Reverse("The quick brown 狐 jumped over the lazy 犬")))
}
return string(runes)
không hoạt động cho mọi trường hợp.
Có quá nhiều câu trả lời ở đây. Một số trong số chúng là bản sao rõ ràng. Nhưng ngay cả từ bên trái, thật khó để chọn ra giải pháp tốt nhất.
Vì vậy, tôi đã xem qua các câu trả lời, loại bỏ một trong những không hoạt động cho unicode và cũng loại bỏ các bản sao. Tôi chuẩn những người sống sót để tìm ra người nhanh nhất. Vì vậy, đây là kết quả với sự phân bổ (nếu bạn nhận thấy câu trả lời mà tôi đã bỏ lỡ, nhưng đáng để thêm vào, vui lòng sửa đổi điểm chuẩn):
Benchmark_rmuller-4 100000 19246 ns/op
Benchmark_peterSO-4 50000 28068 ns/op
Benchmark_russ-4 50000 30007 ns/op
Benchmark_ivan-4 50000 33694 ns/op
Benchmark_yazu-4 50000 33372 ns/op
Benchmark_yuku-4 50000 37556 ns/op
Benchmark_simon-4 3000 426201 ns/op
Vì vậy, đây là phương pháp nhanh nhất bằng rmuller :
func Reverse(s string) string {
size := len(s)
buf := make([]byte, size)
for start := 0; start < size; {
r, n := utf8.DecodeRuneInString(s[start:])
start += n
utf8.EncodeRune(buf[size-start:], r)
}
return string(buf)
}
Vì một số lý do, tôi không thể thêm điểm chuẩn, vì vậy bạn có thể sao chép nó từ PlayGround(bạn không thể chạy thử nghiệm ở đó). Đổi tên nó và chạygo test -bench=.
Tôi đã viết Reverse
hàm sau tuân theo mã hóa UTF8 và các ký tự kết hợp:
// Reverse reverses the input while respecting UTF8 encoding and combined characters
func Reverse(text string) string {
textRunes := []rune(text)
textRunesLength := len(textRunes)
if textRunesLength <= 1 {
return text
}
i, j := 0, 0
for i < textRunesLength && j < textRunesLength {
j = i + 1
for j < textRunesLength && isMark(textRunes[j]) {
j++
}
if isMark(textRunes[j-1]) {
// Reverses Combined Characters
reverse(textRunes[i:j], j-i)
}
i = j
}
// Reverses the entire array
reverse(textRunes, textRunesLength)
return string(textRunes)
}
func reverse(runes []rune, length int) {
for i, j := 0, length-1; i < length/2; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
}
// isMark determines whether the rune is a marker
func isMark(r rune) bool {
return unicode.Is(unicode.Mn, r) || unicode.Is(unicode.Me, r) || unicode.Is(unicode.Mc, r)
}
Tôi đã cố gắng hết sức để làm cho nó hiệu quả và dễ đọc nhất có thể. Ý tưởng rất đơn giản, lướt qua các chữ rune để tìm kiếm các ký tự kết hợp sau đó đảo ngược các chữ cái được kết hợp tại chỗ. Khi chúng ta đã bao phủ tất cả, hãy đảo ngược các chữ rune của toàn bộ chuỗi cũng tại chỗ.
Giả sử chúng tôi muốn đảo ngược chuỗi này bròwn
. Dấu ò
được đại diện bởi hai rune, một cho o
và một cho unicode này \u0301a
đại diện cho "mộ".
Để đơn giản, hãy biểu diễn chuỗi như thế này bro'wn
. Điều đầu tiên chúng tôi làm là tìm kiếm các ký tự kết hợp và đảo ngược chúng. Vì vậy, bây giờ chúng ta có chuỗi br'own
. Cuối cùng, chúng tôi đảo ngược toàn bộ chuỗi và kết thúc bằng nwo'rb
. Điều này được trả lại cho chúng tôi nhưnwòrb
Bạn có thể tìm thấy nó ở đây https://github.com/shomali11/util nếu bạn muốn sử dụng nó.
Dưới đây là một số trường hợp thử nghiệm để hiển thị một số trường hợp khác nhau:
func TestReverse(t *testing.T) {
assert.Equal(t, Reverse(""), "")
assert.Equal(t, Reverse("X"), "X")
assert.Equal(t, Reverse("b\u0301"), "b\u0301")
assert.Equal(t, Reverse("😎⚽"), "⚽😎")
assert.Equal(t, Reverse("Les Mise\u0301rables"), "selbare\u0301siM seL")
assert.Equal(t, Reverse("ab\u0301cde"), "edcb\u0301a")
assert.Equal(t, Reverse("This `\xc5` is an invalid UTF8 character"), "retcarahc 8FTU dilavni na si `�` sihT")
assert.Equal(t, Reverse("The quick bròwn 狐 jumped over the lazy 犬"), "犬 yzal eht revo depmuj 狐 nwòrb kciuq ehT")
}
Xây dựng dựa trên đề xuất ban đầu của Stephan202 và dường như hoạt động đối với các chuỗi unicode:
import "strings";
func Reverse( orig string ) string {
var c []string = strings.Split( orig, "", 0 );
for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 {
c[i], c[j] = c[j], c[i]
}
return strings.Join( c, "" );
}
Thay thế, không sử dụng gói chuỗi, nhưng không phải 'unicode-safe':
func Reverse( s string ) string {
b := make([]byte, len(s));
var j int = len(s) - 1;
for i := 0; i <= j; i++ {
b[j-i] = s[i]
}
return string ( b );
}
//Reverse reverses string using strings.Builder. It's about 3 times faster
//than the one with using a string concatenation
func Reverse(in string) string {
var sb strings.Builder
runes := []rune(in)
for i := len(runes) - 1; 0 <= i; i-- {
sb.WriteRune(runes[i])
}
return sb.String()
}
//Reverse reverses string using string
func Reverse(in string) (out string) {
for _, r := range in {
out = string(r) + out
}
return
}
BenchmarkReverseStringConcatenation-8 1000000 1571 ns/op 176 B/op 29 allocs/op
BenchmarkReverseStringsBuilder-8 3000000 499 ns/op 56 B/op 6 allocs/op
Sử dụng chuỗi.Builder nhanh hơn khoảng 3 lần so với việc sử dụng nối chuỗi
Ở đây khá khác biệt, tôi sẽ nói cách tiếp cận chức năng hơn, không được liệt kê trong số các câu trả lời khác:
func reverse(s string) (ret string) {
for _, v := range s {
defer func(r rune) { ret += string(r) }(v)
}
return
}
ret
được giữ trong trạng thái đóng để xử lý thêm, bởi mỗi hàm trì hoãn.
Đây là cách thực hiện nhanh nhất
func Reverse(s string) string {
size := len(s)
buf := make([]byte, size)
for start := 0; start < size; {
r, n := utf8.DecodeRuneInString(s[start:])
start += n
utf8.EncodeRune(buf[size-start:], r)
}
return string(buf)
}
const (
s = "The quick brown 狐 jumped over the lazy 犬"
reverse = "犬 yzal eht revo depmuj 狐 nworb kciuq ehT"
)
func TestReverse(t *testing.T) {
if Reverse(s) != reverse {
t.Error(s)
}
}
func BenchmarkReverse(b *testing.B) {
for i := 0; i < b.N; i++ {
Reverse(s)
}
}
Mã này giữ nguyên các chuỗi kết hợp các ký tự và cũng sẽ hoạt động với đầu vào UTF-8 không hợp lệ.
package stringutil
import "code.google.com/p/go.text/unicode/norm"
func Reverse(s string) string {
bound := make([]int, 0, len(s) + 1)
var iter norm.Iter
iter.InitString(norm.NFD, s)
bound = append(bound, 0)
for !iter.Done() {
iter.Next()
bound = append(bound, iter.Pos())
}
bound = append(bound, len(s))
out := make([]byte, 0, len(s))
for i := len(bound) - 2; i >= 0; i-- {
out = append(out, s[bound[i]:bound[i+1]]...)
}
return string(out)
}
Nó có thể hiệu quả hơn một chút nếu các nguyên thủy unicode / chuẩn cho phép lặp qua các ranh giới của một chuỗi mà không cần phân bổ. Xem thêm https://code.google.com/p/go/issues/detail?id=9055 .
[]byte
sang string
Go sẽ thay thế "đầu vào UTF-8 không hợp lệ" bằng một điểm mã hợp lệ \uFFFD
.
string
không tồn tại. Nhưng nó có thể tồn tại trong một []byte
.
Nếu bạn cần xử lý các cụm grapheme, hãy sử dụng mô-đun unicode hoặc regexp.
package main
import (
"unicode"
"regexp"
)
func main() {
str := "\u0308" + "a\u0308" + "o\u0308" + "u\u0308"
println("u\u0308" + "o\u0308" + "a\u0308" + "\u0308" == ReverseGrapheme(str))
println("u\u0308" + "o\u0308" + "a\u0308" + "\u0308" == ReverseGrapheme2(str))
}
func ReverseGrapheme(str string) string {
buf := []rune("")
checked := false
index := 0
ret := ""
for _, c := range str {
if !unicode.Is(unicode.M, c) {
if len(buf) > 0 {
ret = string(buf) + ret
}
buf = buf[:0]
buf = append(buf, c)
if checked == false {
checked = true
}
} else if checked == false {
ret = string(append([]rune(""), c)) + ret
} else {
buf = append(buf, c)
}
index += 1
}
return string(buf) + ret
}
func ReverseGrapheme2(str string) string {
re := regexp.MustCompile("\\PM\\pM*|.")
slice := re.FindAllString(str, -1)
length := len(slice)
ret := ""
for i := 0; i < length; i += 1 {
ret += slice[length-1-i]
}
return ret
}
str
đầu ra được trích dẫn, nó sẽ sửa đổi báo giá hàng đầu!
Bạn cũng có thể nhập một triển khai hiện có:
import "4d63.com/strrev"
Sau đó:
strrev.Reverse("abåd") // returns "dåba"
Hoặc để đảo ngược một chuỗi bao gồm các ký tự kết hợp unicode:
strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"
Các triển khai này hỗ trợ thứ tự chính xác của các ký tự đa byte unicode và lược bỏ khi đảo ngược.
Lưu ý: Các hàm đảo ngược chuỗi tích hợp trong nhiều ngôn ngữ lập trình không bảo toàn việc kết hợp và việc xác định các ký tự kết hợp đòi hỏi nhiều thời gian thực thi hơn.
Nó chắc chắn không phải là giải pháp hiệu quả nhất về bộ nhớ, nhưng đối với một giải pháp an toàn UTF-8 "đơn giản", những điều sau đây sẽ hoàn thành công việc và không làm hỏng rune.
Theo ý kiến của tôi, đó là nội dung dễ đọc và dễ hiểu nhất trên trang.
func reverseStr(str string) (out string) {
for _, s := range str {
out = string(s) + out
}
return
}
Hai phương pháp sau chạy nhanh hơn so với giải pháp nhanh nhất giúp duy trì việc kết hợp các ký tự , mặc dù điều đó không có nghĩa là tôi đang thiếu thứ gì đó trong thiết lập điểm chuẩn của mình.
//input string s
bs := []byte(s)
var rs string
for len(bs) > 0 {
r, size := utf8.DecodeLastRune(bs)
rs += fmt.Sprintf("%c", r)
bs = bs[:len(bs)-size]
} // rs has reversed string
Phương pháp thứ hai lấy cảm hứng từ điều này
//input string s
bs := []byte(s)
cs := make([]byte, len(bs))
b1 := 0
for len(bs) > 0 {
r, size := utf8.DecodeLastRune(bs)
d := make([]byte, size)
_ = utf8.EncodeRune(d, r)
b1 += copy(cs[b1:], d)
bs = bs[:len(bs) - size]
} // cs has reversed bytes
LƯU Ý: Câu trả lời này là từ năm 2009, vì vậy có lẽ hiện tại có nhiều giải pháp tốt hơn.
Trông hơi 'vòng vo', và có lẽ không hiệu quả lắm, nhưng minh họa cách giao diện Reader có thể được sử dụng để đọc từ các chuỗi. IntVectors cũng có vẻ rất thích hợp làm bộ đệm khi làm việc với chuỗi utf8.
Nó sẽ thậm chí còn ngắn hơn khi bỏ đi phần 'kích thước' và chèn vào vectơ bằng Chèn, nhưng tôi đoán điều đó sẽ kém hiệu quả hơn, vì toàn bộ vectơ sau đó cần được đẩy lùi lại mỗi lần một chữ rune mới được thêm vào .
Giải pháp này chắc chắn hoạt động với các ký tự utf8.
package main
import "container/vector";
import "fmt";
import "utf8";
import "bytes";
import "bufio";
func
main() {
toReverse := "Smørrebrød";
fmt.Println(toReverse);
fmt.Println(reverse(toReverse));
}
func
reverse(str string) string {
size := utf8.RuneCountInString(str);
output := vector.NewIntVector(size);
input := bufio.NewReader(bytes.NewBufferString(str));
for i := 1; i <= size; i++ {
rune, _, _ := input.ReadRune();
output.Set(size - i, rune);
}
return string(output.Data());
}
Một phiên bản mà tôi nghĩ hoạt động trên unicode. Nó được xây dựng dựa trên các hàm utf8.Rune:
func Reverse(s string) string {
b := make([]byte, len(s));
for i, j := len(s)-1, 0; i >= 0; i-- {
if utf8.RuneStart(s[i]) {
rune, size := utf8.DecodeRuneInString(s[i:len(s)]);
utf8.EncodeRune(rune, b[j:j+size]);
j += size;
}
}
return string(b);
}
rune là một loại, vì vậy hãy sử dụng nó. Hơn nữa, Go không sử dụng dấu chấm phẩy.
func reverse(s string) string {
l := len(s)
m := make([]rune, l)
for _, c := range s {
l--
m[l] = c
}
return string(m)
}
func main() {
str := "the quick brown 狐 jumped over the lazy 犬"
fmt.Printf("reverse(%s): [%s]\n", str, reverse(str))
}
hãy thử mã dưới đây:
package main
import "fmt"
func reverse(s string) string {
chars := []rune(s)
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
chars[i], chars[j] = chars[j], chars[i]
}
return string(chars)
}
func main() {
fmt.Printf("%v\n", reverse("abcdefg"))
}
để biết thêm thông tin, hãy kiểm tra http://golangcookbook.com/cha Chapter/strings/reverse/
và http://www.dotnetperls.com/reverse-string-go
Đối với các chuỗi đơn giản, có thể sử dụng cấu trúc như vậy:
func Reverse(str string) string {
if str != "" {
return Reverse(str[1:]) + str[:1]
}
return ""
}
Đây là một giải pháp khác:
func ReverseStr(s string) string {
chars := []rune(s)
rev := make([]rune, 0, len(chars))
for i := len(chars) - 1; i >= 0; i-- {
rev = append(rev, chars[i])
}
return string(rev)
}
Tuy nhiên, giải pháp của yazu ở trên thanh lịch hơn vì anh ta đảo ngược []rune
lát cắt tại chỗ.
Tuy nhiên, một giải pháp khác (tm):
package main
import "fmt"
type Runes []rune
func (s Runes) Reverse() (cp Runes) {
l := len(s); cp = make(Runes, l)
// i <= 1/2 otherwise it will mess up with odd length strings
for i := 0; i <= l/2; i++ {
cp[i], cp[l-1-i] = s[l-1-i], s[i]
}
return cp
}
func (s Runes) String() string {
return string(s)
}
func main() {
input := "The quick brown 狐 jumped over the lazy 犬 +odd"
r := Runes(input)
output := r.Reverse()
valid := string(output.Reverse()) == input
fmt.Println(len(r), len(output), r, output.Reverse(), valid)
}
package reverseString
import "strings"
// ReverseString - output the reverse string of a given string s
func ReverseString(s string) string {
strLen := len(s)
// The reverse of a empty string is a empty string
if strLen == 0 {
return s
}
// Same above
if strLen == 1 {
return s
}
// Convert s into unicode points
r := []rune(s)
// Last index
rLen := len(r) - 1
// String new home
rev := []string{}
for i := rLen; i >= 0; i-- {
rev = append(rev, string(r[i]))
}
return strings.Join(rev, "")
}
Kiểm tra
package reverseString
import (
"fmt"
"strings"
"testing"
)
func TestReverseString(t *testing.T) {
s := "GO je úžasné!"
r := ReverseString(s)
fmt.Printf("Input: %s\nOutput: %s", s, r)
revR := ReverseString(r)
if strings.Compare(s, revR) != 0 {
t.Errorf("Expecting: %s\n. Got: %s\n", s, revR)
}
}
Đầu ra
Input: GO je úžasné!
Output: !énsažú ej OG
PASS
ok github.com/alesr/reverse-string 0.098s
a+´
thay vì choá
. Tôi tự hỏi làm thế nào điều đó có thể được tính đến, mà không cần bình thường hóa nó.