劍指offer【40~49】

題目鏈接:

劍指offer 40-49


目錄:

40. 最小的 K 個數(shù)
41.1 數(shù)據(jù)流中的中位數(shù)
41.2 字符流中第一個不重復(fù)的字符
42. 連續(xù)子數(shù)組的最大和
43. 從 1 到 n 整數(shù)中 1 出現(xiàn)的次數(shù)
44. 數(shù)字序列中的某一位數(shù)字
45. 把數(shù)組排成最小的數(shù)
46. 把數(shù)字翻譯成字符串
47. 禮物的最大價值
48. 最長不含重復(fù)字符的子字符串
49. 丑數(shù)


Python 實現(xiàn):

40. 最小的 K 個數(shù)
  • 快排或者堆排序,全排猾骡,時間復(fù)雜度為 O(n*logn)瑞躺,pass敷搪;
  • 方法一:實際上只需要維護一個大小為 k 的大根堆,時間復(fù)雜度最壞為 O(k*logk) + O((n-k)*logk) = O(n*logk)幢哨,這對于海量數(shù)據(jù)是非成目保可取的。代碼如下:
# -*- coding:utf-8 -*-
import heapq
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        if k == 0 or k > len(tinput):
            return []
        heap = []
        for i in range(len(tinput)):
            if len(heap) < k:  # 建堆(k*logk)
                heapq.heappush(heap, -tinput[i])  # 取負號是為了建立大根堆
            elif len(heap) == k:   # 調(diào)整堆((n-k)*logk)
                if heap[0] < -tinput[i]:
                    heapq.heapreplace(heap, -tinput[i])
        return [-h for h in heapq.nlargest(k, heap)]

更簡單一些:

# -*- coding:utf-8 -*-
import heapq
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        return heapq.nsmallest(k, tinput) if k <= len(tinput) else []
  • 方法二:利用快排的 partition 函數(shù)捞镰,能夠做到時間復(fù)雜度為 O(n)闸与,但是有缺點:(1)需要修改原數(shù)組;(2)找到的 k 個數(shù)不是排好序的岸售;(3)海量數(shù)據(jù)是不可取的践樱。代碼如下:
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        # 劃分
        def partition(lo, hi):
            r, i, j = tinput[lo], lo + 1, hi
            while i <= j:
                while i <= j and tinput[i] < r:
                    i += 1
                while i <= j and tinput[j] > r:
                    j -= 1
                if i <= j:
                    tinput[i], tinput[j] = tinput[j], tinput[i]
                    i += 1
                    j -= 1
            tinput[lo], tinput[j] = tinput[j], tinput[lo]
            return j
        
        # 查找,這里使用遞歸凸丸,也可以使用迭代的方法
        def find(k, lo, hi):
            ind = partition(lo, hi)
            if k == ind + 1:  # 此時數(shù)組中前 k 個數(shù)就是最小的了
                return
            elif k < ind + 1:
                find(k, lo, ind - 1)
            elif k > ind + 1:
                find(k, ind + 1, hi)
        
        if k == 0 or k > len(tinput):
            return []
        find(k, 0, len(tinput) - 1)
        return sorted(tinput[:k])  # 前 k 個數(shù)排序后再輸出拷邢,不然報錯

41.1 數(shù)據(jù)流中的中位數(shù)
  • 使用堆,左邊維護一個大根堆屎慢,其根存儲小于中位數(shù)的最大的數(shù)瞭稼,右邊維護一個小根堆前普,其根存儲大于等于中位數(shù)的最小的數(shù)裁良。再使用一個變量記錄插入的總數(shù) size。
  • 每次插入時揭璃,根據(jù) size 分奇偶妖枚,對兩個堆進行操作廷臼,始終保持上述性質(zhì)。
  • 每次查找中位數(shù)時绝页,還是根據(jù) size 分奇偶。如果 size 為偶數(shù)寂恬,則兩個堆的根的平均值就是中位數(shù)续誉;size 為奇數(shù),右邊的小根堆的根就是中位數(shù)(因為右邊的堆比左邊的堆的數(shù)目多 1)初肉。

時間復(fù)雜度為:插入為 O(logn)酷鸦,計算中位數(shù)為 O(1);空間復(fù)雜度:O(n)牙咏。

# -*- coding:utf-8 -*-
import heapq
class Solution:
    def __init__(self):
        self.left = []  # 左邊大根堆存較小的數(shù)
        self.right = []  # 右邊小根堆存較大的數(shù)
        self.size = 0  # 數(shù)的個數(shù)臼隔,如果為奇數(shù)個,right 比 left 多一個
    
    def Insert(self, num):
        # write code here
        if self.size % 2 == 0:
            heapq.heappush(self.left, -num)
            heapq.heappush(self.right, -heapq.heappop(self.left))
        else:
            heapq.heappush(self.right, num)
            heapq.heappush(self.left, -heapq.heappop(self.right))
        self.size += 1
        
    def GetMedian(self, a):  # 這里隨便寫個 a妄壶,是因為函數(shù)頭給錯了摔握,防止錯誤
        # write code here
        if self.size == 0:
            return 0
        if self.size % 2 == 0:
            return (-self.left[0] + self.right[0]) / 2.0
        else:
            return self.right[0]
41.2 字符流中第一個不重復(fù)的字符

使用隊列,同時使用一個一維數(shù)組記錄每個字符出現(xiàn)的次數(shù)丁寄。如果在插入后某個字符出現(xiàn)次數(shù)超過 1 次氨淌,就從隊列頭部刪除該數(shù)泊愧,這樣可以保證隊列頭部始終是第一個出現(xiàn) 1 次的字符。

時間復(fù)雜度:插入為 O(n)盛正,查找為 O(1)删咱;空間復(fù)雜度為:O(n)。

# -*- coding:utf-8 -*-
import collections
class Solution:
    # 返回對應(yīng)char
    def __init__(self):
        self.q = collections.deque()
        self.cnt = [0] * 256  # 存儲字符出現(xiàn)的次數(shù)豪筝,使用對應(yīng)的 ASCLL 下標
    
    def FirstAppearingOnce(self):
        # write code here
        return self.q[0] if self.q else "#"
        
    def Insert(self, char):
        # write code here
        self.cnt[ord(char)] += 1
        self.q.append(char)
        while self.q and self.cnt[ord(self.q[0])] > 1:  # 將超過一次的字符刪除
            self.q.popleft()

42. 連續(xù)子數(shù)組的最大和
  • 動態(tài)規(guī)劃水題痰滋,轉(zhuǎn)移方程為:dp[i] = max(array[i], dp[i-1] + array[i]),其中 dp[i] 表示以 i 為結(jié)尾的最大子段和续崖。最后 max(dp) 即為答案敲街。
  • 可以使用兩個變量完成,一個 tmp 記錄局部累加最大值袜刷,一個 ans 記錄全局最大值聪富。tmp > 0 累加 arrayp[i],否則 tmp = array[i]著蟹。每次更新 ans = max(ans, tmp)墩蔓,最后 ans 就是答案。
# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        ans = tmp = array[0]  # ans 為全局最大值萧豆,tmp 為局部最大值
        for i in range(1, len(array)):
            if tmp > 0:
                tmp += array[i]
            else:
                tmp = array[i]
            ans = max(ans, tmp)
        return ans

43. 從 1 到 n 整數(shù)中 1 出現(xiàn)的次數(shù)

首先奸披,暴力不用想肯定超時(n*logn),pass涮雷。那么就從數(shù)學(xué)上推導(dǎo):

  • 思想:將每一位上1出現(xiàn)的次數(shù)加起來阵面,就是所求的總次數(shù)了。
  • 我們以百位為例子洪鸭,在 12x45 中样刷,百位為 x ,那么百位前的數(shù)字為 12览爵,百位后的數(shù)字為 45置鼻。此時分為 3 種情況:
    (1)x == 0,這時候后面的數(shù)字對百位上 1 的出現(xiàn)次數(shù)是沒有影響的蜓竹,只受前面數(shù)字的影響箕母,即: 12 * 100,100 為百位的位數(shù)俱济。
    (2)x == 1嘶是,此時既受前面數(shù)字的影響也受后面數(shù)字的影響,因為在 12 * 100 后蛛碌,1 又出現(xiàn)了后面數(shù)字 +1 那么多次(從 1210012145 )聂喇,即 12 * 100 + 45 + 1
    (3)x > 1左医,此時因為必然包含 12100-12199 共100(百位的位數(shù))個 1授帕,所以百位上 1 出現(xiàn)的次數(shù)也與后面的數(shù)字沒有關(guān)系同木,為 12 * 100 + 100(12 + 1) * 100
  • 因此跛十,當前位 cur_bit 中 1 出現(xiàn)的次數(shù)如下

時間復(fù)雜度為 O(logn)彤路,空間復(fù)雜度為 O(1)。

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        ans, i = 0, 1
        while i <= n:
            high, low = divmod(n, i)
            cur_bit = high % 10
            if cur_bit == 0:
                ans += high // 10 * i
            elif cur_bit == 1:
                ans += (high // 10)* i + (low + 1)
            elif cur_bit > 1:
                ans += (high // 10 + 1) * i
            i *= 10
        return ans

44. 數(shù)字序列中的某一位數(shù)字
  • 觀察規(guī)律芥映,構(gòu)造一個 bits = [9, 180, 2700, 36000, ...]洲尊,表示長度為 (i + 1) 的數(shù)字總共有 bits[i] 位;再求 presums 為 bits 的前綴和奈偏,表示長度為 (i + 1) 的數(shù)字前面總共有 bits[i] 位坞嘀。
  • 根據(jù) presum 來確定第 n 位所代表的數(shù)字 x 位于哪個區(qū)間(即 x 是幾位數(shù)),然后計算 x 在該區(qū)間中的偏移量即可惊来。
class Solution:
    def findNthDigit(self, n: int) -> int:
        if n < 10:
            return n
        bits = [9 * (10 ** i) * (i + 1) for i in range(8)]
        presum = [0] * len(bits)  # 前面有多少位
        presum[0] = bits[0]
        for i in range(1, len(bits)):
            presum[i] = presum[i-1] + bits[i] 
        i = len(presum) - 1
        while i >= 0:  # 找到第n位所代表數(shù)字的區(qū)間
            if n >= presum[i]:
                n -= presum[i]
                break
            i -= 1
        pres = n // (i + 2)  # i+2 為該數(shù)字的長度
        n = n - (i + 2) * pres  # 該數(shù)字的偏移量
        num = (10 ** (i + 1)) + pres  # 該數(shù)字前面有num個數(shù)
        if n > 0:
            return str(num)[n-1]
        else:
            return str(num-1)[-1]

45. 把數(shù)組排成最小的數(shù)

按照 str1+str2 < str2+str1 的排序規(guī)則進行排序即可丽涩。如 32 排在 326 的前面,3 排在 32 的后面裁蚁。注意 Python 的 cmp 寫法和 C++ 不同矢渊。

# -*- coding:utf-8 -*-
from functools import cmp_to_key  # Python3 要實現(xiàn) cmp 需要導(dǎo)入這個
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        def cmp(a, b):  # 參數(shù) a, b 的順序和 C++ 的相反,所以下面是 >
            return 1 if a + b > b + a else -1  # 返回的是 1 和 -1枉证,而不是 1 和 0
        snums = [str(num) for num in numbers]
        snums.sort(key=cmp_to_key(cmp))  # 排序規(guī)則的傳遞方法
        return "".join(snums)

如果不會這種 Python3 的 cmp 實現(xiàn)方法矮男,可以自己寫一個快排來是實現(xiàn)自定義排序規(guī)則:

# -*- coding:utf-8 -*-
class Solution:  
    # 自定義排序規(guī)則加快排
    def PrintMinNumber(self, numbers):
        # write code here
        if not numbers:
            return ""
        numbers = map(str, numbers)
        pivot = numbers[0]
        less = [num for num in numbers[1:] if (pivot+num)>(num+pivot)]
        great = [num for num in numbers[1:] if (pivot+num)<=(num+pivot)]
        ans = "".join(self.PrintMinNumber(less))+ pivot + "".join(self.PrintMinNumber(great))
        return ans

46. 把數(shù)字翻譯成字符串

舉幾個例子,找規(guī)律室谚,發(fā)現(xiàn)是斐波那契數(shù)列毡鉴。

  • 設(shè) dp[len(s) + 1],初始值除了 dp[0] = 1 外秒赤,其他都設(shè)置為 0猪瞬。dp[0] = 1 是出口)。dp[i] 表示以 s[i-1] 結(jié)尾的編碼數(shù)量入篮,則 dp[lens] 就是答案撑螺;
  • 注意:這道題編碼是 1~26,因此要排除 == 0> 26 的情況崎弃。當前位 s[i-1] != '0' 時,dp[i] = dp[i-1]含潘,如果 s[i-1] 和前一個字符 s[i-2] 組成的數(shù)字在 10 ~ 26 之間饲做,則 dp[i] 還需要累加 dp[i-2],即 dp[i] += dp[i-2]遏弱。
class Solution:
    def numDecodings(self, s: str) -> int:
        if not s: 
            return 0
        lens = len(s)
        dp = [1] + [0] * lens
        for i in range(1, lens + 1):
            if s[i-1] != '0':   # 當前位不為 0
                dp[i] = dp[i-1]
            if i >= 2 and 10 <= int(s[i-2: i]) <= 26:   # 能組成兩位(10~26)
                dp[i] += dp[i-2]
        return dp[lens]

47. 禮物的最大價值

動態(tài)規(guī)劃:dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + board[i-1][j-1]盆均,最后 dp[-1][-1] 就是答案。

# -*- coding:utf-8 -*-
class Bonus:
    def getMost(self, board):
        # write code here
        dp = [[0] * (len(board[0]) + 1) for _ in range(len(board) + 1)]
        for i in range(1, len(board) + 1):
            for j in range(1, len(board[0]) + 1):
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + board[i-1][j-1]
        return dp[-1][-1]

48. 最長不含重復(fù)字符的子字符串

方法1:使用隊列漱逸,記錄不重復(fù)的連續(xù)子串泪姨。碰到一個字符在隊列中出現(xiàn)過游沿,更新最大長度,并從隊列頭部進行刪除操作肮砾,直到碰到該字符(實際上可以理解為滑動窗口)诀黍。時間復(fù)雜度為 O(n^2),空間復(fù)雜度為 O(n)仗处。在 Leetcode 第 3 題中擊敗了 88.56% 的 Python3 提交眯勾。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        maxl = 0
        q = collections.deque()
        for i in range(len(s)):
            if s[i] in q:
                maxl = max(maxl, len(q))  # 更新最大長度
                while q and q[0] != s[i]:  # 從隊列頭部刪除,直到s[i]
                    q.popleft()
                q.popleft()  # 刪除
            q.append(s[i])
        return max(len(q), maxl)  # 最后一段可能是最大長度

方法2:哈希表 Hash Table + 滑動窗口婆誓。使用一個變量 left 記錄窗口左界吃环,每次碰到窗口內(nèi)的字符,更新最大長度洋幻,同時更新 left 為當前字符位置 +1郁轻,即滑動窗口左界到下一個位置。時間復(fù)雜度為 O(n)文留,空間復(fù)雜度為 O(n)好唯。在 Leetcode 第 3 題中擊敗了 94.13% 的 Python3 提交。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        maxl = 0
        dic = dict()
        left = 0  # 記錄滑動窗口的左界
        for i in range(len(s)):
            if s[i] in dic and dic[s[i]] >= left:  # s[i] 必須在滑動窗口內(nèi)
                maxl = max(maxl, i - left)  # 更新最大長度
                left = dic[s[i]] + 1  # 更新滑動窗口的左界
            dic[s[i]] = i
        return max(maxl, len(s) - left)  # 最后一段可能是最大長度

49. 丑數(shù)

方法1:使用集合只保存丑數(shù)厂庇,對于一個較小的丑數(shù)渠啊,把其 ×2、×3 和 ×5 的值也保存在集合中权旷,每次從集合中選一個最小值并移除替蛉。時間復(fù)雜度較高,但能 AC拄氯。

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index == 0:
            return 0
        ans = 1
        sets = {1}
        for i in range(index):
            ans = min(sets)
            sets.add(ans * 2)
            sets.add(ans * 3)
            sets.add(ans * 5)
            sets.remove(ans)
        return ans

方法2:動態(tài)規(guī)劃躲查。用 dp[i] 表示第 i 個丑數(shù)的值,則 dp[index] 就是答案译柏。

  • 丑數(shù)序列是 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, …
  • 因為丑數(shù)序列是通過乘以 2, 3, 5 構(gòu)建镣煮,所以可以構(gòu)建三個序列,每次取其中最小的鄙麦,序列的構(gòu)建是因子乘以丑數(shù)序列中的數(shù)典唇。
    (1) 1×2, 2×2, 3×2, 4×2, 5×2, …
    (2) 1×3, 2×3, 3×3, 4×3, 5×3, …
    (3) 1×5, 2×5, 3×5, 4×5, 5×5, …
  • 因此,可以使用 3 個指針 ind2胯府、ind3介衔、ind5 分別指向最后一次進行 ×2,×3骂因,×5 操作后的位置炎咖。在找下一個丑數(shù)的時候,一定會是這三個指針指向的丑數(shù) ×2、×3乘盼、×5 中的最小數(shù) val升熊。同時,將指向 val 的指針(ind2绸栅、ind3级野、ind5 至少其中一個)向后移動一位,就能一直生成下一個丑數(shù)阴幌,直到得到答案 dp[index]勺阐。
  • 注意:一次最少更新一個指針(如遇到第 n 個丑數(shù)是 6 時,ind2 和ind3 都要更新)矛双。

時間復(fù)雜度和空間復(fù)雜度均為 O(n)渊抽。

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        dp = [0] + [1] * index   # 第 0 個丑數(shù)的值為 0
        ind2 = ind3 = ind5 = 1
        for i in range(2, index + 1):
            # 下一個丑數(shù)取決于 3 個指針指向的丑數(shù)分別乘以 2,3议忽,5 中的最小值
            val = min(dp[ind2] * 2, dp[ind3] * 3, dp[ind5] * 5)  
            if val == dp[ind2] * 2:  # 更新指針移動到下一位
                ind2 += 1
            if val == dp[ind3] * 3:  # 這里不能使用 elif懒闷,因為可能也會移動
                ind3 += 1
            if val == dp[ind5] * 5:  # 這里不能使用 elif,因為可能也會移動
                ind5 += 1
            dp[i] = val  # 第 i 給丑數(shù)就是 val
        return dp[index]  # 或者 dp[-1] 是答案

劍指 offer 終于過了一遍栈幸,大多數(shù)題目還是很簡單的愤估,但是題目具有代表性,涉及鏈表速址、數(shù)組玩焰、深搜回溯、字符串芍锚、數(shù)組昔园、數(shù)學(xué)、位運算并炮、動態(tài)規(guī)劃等默刚。這里做一個總結(jié):

劍指offer【03~09】
劍指offer【10~19】
劍指offer【20~29】
劍指offer【30~39】
劍指offer【40~49】
劍指offer【50~59】
劍指offer【60~68】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市逃魄,隨后出現(xiàn)的幾起案子荤西,更是在濱河造成了極大的恐慌,老刑警劉巖伍俘,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邪锌,死亡現(xiàn)場離奇詭異,居然都是意外死亡癌瘾,警方通過查閱死者的電腦和手機秃流,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柳弄,“玉大人,你說我怎么就攤上這事”套ⅲ” “怎么了嚣伐?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長萍丐。 經(jīng)常有香客問我轩端,道長,這世上最難降的妖魔是什么逝变? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任基茵,我火速辦了婚禮,結(jié)果婚禮上壳影,老公的妹妹穿的比我還像新娘拱层。我一直安慰自己,他們只是感情好宴咧,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布根灯。 她就那樣靜靜地躺著,像睡著了一般掺栅。 火紅的嫁衣襯著肌膚如雪烙肺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天氧卧,我揣著相機與錄音桃笙,去河邊找鬼。 笑死沙绝,一個胖子當著我的面吹牛搏明,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宿饱,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼熏瞄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谬以?” 一聲冷哼從身側(cè)響起强饮,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎为黎,沒想到半個月后邮丰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡铭乾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年剪廉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炕檩。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡斗蒋,死狀恐怖捌斧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泉沾,我是刑警寧澤捞蚂,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站跷究,受9級特大地震影響姓迅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俊马,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一丁存、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柴我,春花似錦解寝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至彤悔,卻和暖如春嘉抓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晕窑。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工抑片, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杨赤。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓敞斋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親疾牲。 傳聞我的和親對象是個殘疾皇子植捎,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360