形式為a^i b^j c^k的子序列數

2021-02-24 中學生編程與信息學競賽自學

本課程是從少年編程網轉載的課程,目標是向中學生詳細介紹計算機比賽涉及的程式語言,數據結構和算法。編程學習最好使用計算機,請登陸 www.3dian14.org (免費註冊,免費學習)。

給定一個字符串,統計形式為a^i b^j c^k()的子序列數,即由i個'a'字符,後跟j個'b'個字符,然後再跟k個'c'個字符組成,其中i>=1,j>=1和k>=1。

注意:即使兩個子序列相同,只要兩個子序列中至少有一個字符來自於原字符串的不同位置,也認為這兩個子序列是不同的。

【例子1】

輸入  : abbc

輸出 : 3

符合條件的子序列:abc, abc 和 abbc。注意,兩個abc中的b分別來自於原字符串的第二位置和第三個位置。

【例子2】

輸入  : abcabc

輸出 : 7

符合條件的子序列:abc, abc, abbc, aabc, abcc, abc 和 abc


算法

我們遍歷給定的字符串s。對於每個遇到的字符,執行以下操作:

1)設變量aCount為由'a'的不同組合產生的不同子序列的計數。初始化為0。

2)設變量bCount為由'b'的不同組合產生的不同子序列的計數。初始化為0。

3)設變量cCount為由'c'的不同組合產生的不同子序列的計數。初始化為0。

4)遍歷給定字符串的所有字符。對當前字符s[i]執行以下操作

    如果當前字符是'a',則存在以下可能性:

    a)當前字符'a'開始一個新的子序列;

    b)取當前字符'a'添加在已有aCount子序列末尾;

    c)捨去當前字符'a',也就是不把它作為aCount子序列的一部分。

    因此,aCount =(1 + 2 * aCount);

    如果當前字符為'b',則存在以下可能性:

    a)當前字符'b'以aCount子序列(此時序列中僅僅包含字符'a')開始b的新子序列。

    b)取當前字符'b'添加在已有bCount子序列末尾;

    c)捨去當前字符'b',也就是不把它作為bCount子序列的一部分。

    因此,bCount =(aCount + 2 * bCount);

    如果當前字符為'c',則存在以下可能性:

    a)當前字符'c'以bCount子序列(此時序列中僅僅包含字符'a'和'b')開始c的新子序列。

    b)取當前字符'c'添加在已有cCount子序列末尾;

    c)捨去當前字符'c',不把它作為 cCount子序列的一部分。

    因此,cCount =(bCount + 2 * cCount);

5)最後我們返回cCount,它就是最後的結果。

我們用例子來說明該算法:

aCount是字母「 a」的子序列數。

考慮以下示例:aa。

我們可以看到aCount為3:因為我們可以選擇以下可能性:(xa,ax,aa)(x表示我們沒有使用該字符)。還要注意,這與兩個'a'之間的字符無關,即'aa'的aCount和'ccbabbbcac'的aCount是相同的,因為它們的正好都包含2個a。

    

現在,加上1個'a',記住此時之前由字符'a'組成的子序列數為aCount。對應新增加的字符'a', 我們有三種選擇:

   i)還是每個之前由a組成的子序列,不取新增加的'a', 共有aCount個

   ii)每個之前由a組成的子序列+新的字符'a' 組成新序列,共有aCount個

   iii)  單獨由這裡新增加的字母'a'開始的新序列,個數為1

因此,增加一個'a',    新的僅有a組成的子序列數為:aCount + aCount + 1。

現在,讓我們考慮bCount,即幾個'a'之後接幾個'b'組成的子序列數。在"aab"中,我們看到bCount應該為3(對應的序列為axb,xab,aab, x表示沒有使用字符),因為它就是開始是兩個'a'的子序列,再加上'b'。因此,此時bCount的值就等於aCount。

現在我們再增加字符'b', 讓我們找到"aabb"的bCount。我們已經確定"aab"具有3個子序列,因此我們當然仍然具有這些子序列。此外,我們可以將新的b添加到這些子序列中的任何一個上,再次獲得3個新的子序列。最後,我們必須計算在不使用任何其他b的情況下產生的子序列,也就是aa加上當前的b組成的新序列(當前字符b是序列中的第一個字符b),根據上一段中的邏輯,這就等於是aCount。因此,bCount就是舊的bCount * 2 + aCount;

cCount的計算過程與bCount類似。

下面幾個示意圖依次展示了針對字符串"aabbc"計算aCount,bCount和cCount的過程。


算法實現

下面是算法實現的例子。

#include <bits/stdc++.h> 

using namespace std; 

int countSubsequences(string s) 

    int aCount = 0; 

   int bCount = 0; 

   int cCount = 0; 

    // 遍歷字符串 

    for (unsigned int i=0; i<s.size(); i++) 

    { 

        if (s[i] == 'a') 

            aCount = (1 + 2 * aCount); 

        else if (s[i] == 'b') 

            bCount = (aCount + 2 * bCount); 

        else if (s[i] == 'c') 

            cCount = (bCount + 2 * cCount); 

    } 

    return cCount; 

// 主程序 

int main() 

    string s = "aabbc"; 

    cout << countSubsequences(s) << endl; 

    return 0; 

}

請你輸入程序,運行並驗證結果。一旦明白了算法,編程是不是佷簡單?

相關焦點

  • 算法精析:10隻白鼠試喝千瓶水1瓶有毒與最長公共子序列(串)
    四、最長公共子序列(串)算法題目:首先,最長公共子序列(LCS)及最長公共子串(LCSubstr)的定義:a="ABCGEUA"b="C五、最長公共子序列(串)算法思維分析這是一道考察動態規劃在算法中應用的題目,面試中很常見,也是一道經典的算法題。如果沒有見過類似題目的人,可能覺得有點難度。以下為思維線索分析(如果已經有所了解,請跳過):1、求最長公共子序列及子串,可用使用暴力破解法。
  • 經典算法研究總結(5)-動態規划算法解最長公共子序列LCS問題
    其中c[i,j]存儲Xi與Yj的最長公共子序列的長度,b[i,j]記錄指示c[i,j]的值是由哪一個子問題的解達到的,這在構造最長公共子序列時要用到。最後,X和Y的最長公共子序列的長度記錄於c[m,n]中。
  • C語言八大排序算法,附動圖和詳細代碼解釋!
    元素個數N,取奇數k=N/2,將下標差值為k的數分為一組(一組元素個數看總元素個數決定),在組內構成有序序列,再取k=k/2,將下標差值為k的數分為一組,構成有序序列,直到k=1,然後再進行直接插入排序。
  • 序列比對原理
    其計算得分依舊是取 3 個方向來源的最大值,但如果出現負分則修改為 0. 所以其第一行和第一列全部值為 0. 後面回溯也不從右下角回溯到左上角,而是從得分最高的格子回溯到第一個得分為 0 的格子。從而在長序列中尋找最佳匹配的子序列比對。這在生物學上很有用,尋找到高度相似的子序列,可能就是同源序列部分。
  • C程序設計的常用算法
    ; break; } }   五、排序問題   1.選擇法排序(升序)   基本思想: 1)對有n個數的序列(存放在數組a(n)中),從中選出最小的數,與第1個數交換位置; 2)除第1 個數外,其餘n-1個數中選最小的數,與第2個數交換位置
  • c語言快速傅立葉變換
    > const double PI="3".141592653589793;complex u,Wn,t;int i,j,k,m,kind,distance,other; double tmp; for(i=0;i<length;i++) //實現倒敘排列{ k="i"; j=0;
  • leetcode873_go_最長的斐波那契子序列的長度
    如果序列 X_1, X_2, ..., X_n 滿足下列條件,就說它是 斐波那契式 的:n >= 3對於所有 i + 2 <= n,都有 X_i + X_{i+1} = X_{i+2}給定一個嚴格遞增的正整數數組形成序列,找到 A 中最長的斐波那契式的子序列的長度。如果一個不存在,返回 0 。
  • 最長的Zig-Zag交替子序列問題
    【例二】輸入:arr [] = {1, 4, 5}輸出:2長度為2的所有交替子序列為:序列{1,4}滿足 1<4;序列{1,5}滿足 1<5。【例三】輸入:arr [] = {10, 22, 9, 33, 49, 50, 31, 60}輸出:6長度為6的所有交替子序列為:子序列{10, 22, 9, 33, 31, 60}滿足 10<22>9<33>31<60子序列{10, 22, 9, 49, 31, 60}滿足 10<
  • 單片機常用的14個C語言算法
    (任意一個大於等於6的偶數都可以分解為兩個素數之和) 基本思想:n為大於等於6的任一偶數,可分解為n1和n2兩個數,分別檢查n1和n2是否為素數,如都是,則為一組解。; break; } } 五、排序問題  1.選擇法排序(升序)   基本思想: 1)對有n個數的序列(存放在數組a(n)中),從中選出最小的數,與第1個數交換位置; 2)除第1 個數外,其餘n-1個數中選最小的數,與第2個數交換位置; 3)依次類推,選擇了n-1次後,這個數列已按升序排列。
  • 【C/C+】10個經典的C語言小程序,小白必看!
    for (k=1;k { if (i!=k&&i!=j&&j!=k) /*確保i、j、k三位互不相同*/ printf("%d,%d,%d\n",i,j,k); } }
  • 100個經典C語言程序,值得收藏!(上)
    main() { int i,j,k; printf("\n"); for(i=1;i<5;i++)    /*以下為三重循環*/ for(j=1;j<5;j++) for (k=1;k<5;k++) { if (i!=k&&i!=j&&j!
  • 動態規劃解最長公共子序列問題
    seq1,vector<int>lcs , int i,int j,int MaxLen,vector<vector<int>>&LcsSet);public:    /**     * 對外的接口     * 輸入是兩個序列     * 輸出是0-n條最長公共子序列     */
  • 單片機常用的14個C語言算法,看過的據說都晉級高手了
    ;break;}}1.選擇法排序(升序)基本思想:1)對有n個數的序列(存放在數組a(n)中),從中選出最小的數,與第1個數交換位置;2)除第1 個數外,其餘n-1個數中選最小的數,與第2個數交換位置;3)依次類推,選擇了n-1次後,這個數列已按升序排列。
  • C語言實現FFT(快速傅立葉變換)
    輸出參數:a和b的乘積,以聯合體的形式輸出*******************************************************************/struct compx EE(struct compx a,struct compx b){struct compx c;c.real
  • 二分查找的妙用:判定子序列
    對於一個問題,你可能都很難想到它跟二分查找有關,比如前文 最長遞增子序列就藉助一個紙牌遊戲衍生出二分查找解法。今天再講一道巧用二分查找的算法問題:如何判定字符串s是否是字符串t的子序列(可以假定s長度比較小,且t的長度非常大)。
  • CF1000D 又一個序列上的問題
    如果一個序列可以分成正數個好的序列,則稱其為好序列。每個好的序列應該是序列的一個子段,並且序列的每個元素都應該完全屬於一個序列。對於給定的序列,計算其為好序列的子序列的數量,並模 lt;= n; i++)    {        for (int j = 1; j < i; j++)        {            c[i][j] = (c[i-1][j] % mod + c[i-1][j-1] % mod) % mod;        }    }    for (int i = n; i >= 1
  • C語言經典經典必背程序100例
    2.程序原始碼:main(){int i,j,k;printf("\n");for(i=1;i<5;i++)    /*以下為三重循環*/for(j=1;j<5;j++)for(k=1;k<5;k++){if (i!
  • 列印出最長的公共子序列(LCS)
    在前一課中,我們討論了最長公共子序列(LCS)問題,並且討論了如何計算兩個序列的LCS的長度,但是沒有輸出具體的LCS序列。在本課中,我們討論了如何構造和輸出LCS具體序列。【問題】給定兩個序列,請找出兩個序列中存在的最長公共子序列(LCS)。注意:子序列由原序列中的子元素組成,這些子元素的次序與它們在原序列中出現的次序一樣,但不一定連續。
  • 《C語言入門指南》上篇
    -1 //++ 的使用 int i = 10; int j = i++; // 運算規則等價是 int j = i; i = i + 1; ==> j = 10, i=11 int k = ++i; // 運算規則等價 i = i + 1; int k = i; ===> i=12, k =12 printf("\n i=%d j=%d",