利用正則表達式求解多項式相乘問題

最近幾天在學習正則表達式,然後想起來以前一些關於字符串解析的問題都可以利用正則表達式進行求解,而不必人工、機械的去自己一位位去解析目標字符串。比如經典的多項式相乘問題,我們來看一個問題的實例:
http://acm.ecnu.edu.cn/problem/2821/

計算兩個一元多項式的乘積。
Input
每行兩個多項式,以一個空格分隔,多項式格式爲 anx^n+…+a1x+a0。
每行長度不超過 100,0。
Output
每組數據一行,根據次數由高到低順序輸出兩個多項式乘積的非零項係數,兩個係數之間由一個空格分隔。

傳統的方法我們可以按位解析字符串,獲取每個X項的次數及其對應的係數項,然後計算每個結果冪次的係數值即可,參考代碼:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define N 100
void readpoly(char *s, int* co) //讀取多項式的每一項的係數和次方數
{
    while (*s)
    {
        int sign = 1,a = 0,i = 0;
        if (*s == '+' ) s++;    //處理項以+開始
        else if (*s == '-') sign = -1,s++;   //處理項以-開始
        while (isdigit(*s)) {a = a * 10 + *s-'0';s++;} //係數數值
        if (a == 0) a = 1; //係數省略時表示係數爲1
        if (*s != 'x') {co[0] = a * sign; return;} //沒有出現x時表示0次項
        else s++;
        if (*s=='^') s++;
        while (isdigit(*s)) {i = i * 10 + *s-'0';s++;}  //次方數值
        if (i == 0) i = 1; //省略^及次數時表示次方爲1
        co[i] = a * sign;
    }
}

void multipy(char *s1,char *s2, int* co) //兩個多項式的乘法
{
    int co1[N] = {0},co2[N] = {0};
    readpoly(s1,co1);
    readpoly(s2,co2); //獲取多項式的每項係數和次方數
    for (int i = 0; i < N/2; i++)
        for (int j = 0; j < N/2; j++)
            co[i+j] += co1[i] * co2[j];     //x(i次)*x(j次)=x(i+j次)
}

int main()
{
    char s1[N+1],s2[N+1];
    while (scanf("%s%s",s1,s2) == 2)  //讀取兩個多項式,直至讀不到
    {
        int co[N] = {0},out[N],n = 0;
        multipy(s1,s2,co);
        for (int i = 0; i < N; i++) if (co[i]) out[n++] = co[i];  //輸出值不爲0的那些係數
        for (int i = n-1; i >= 0; i--)
        {
            printf("%d",out[i]);
            if (i > 0) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}

下面我們考慮利用正則表達式解析出字符串中各冪次及其對應的係數值,對於一般的形如ax^b項(a爲係數,b爲次方數),我們利用以下正則表達式進行解析:

"([+-]?\d*)[xX]\^([+-]?\d*)"

格式爲:正負符號 + 數字 + x + ^ + 正負符號 + 數字
其中x前的正負符號和數字爲非必需(即考慮到係數項爲1的情況)
此外,我們還需要對不是形如ax^b的項進行解析抽取,即一次項和常數項的情況。
首先我們考慮一種特殊情況,表達式爲純數字的情況:

"^\d+$"

然後我們考慮從字符串中抽取常數項(注意,不同於純數字的情況):

"([+-]+\d+)$"

該表達式意味着我們抽取的目標串爲以 正負符號 + 數字 結尾的子串,其中開頭的正負符號位是承接了更高冪次的項。
最後我們考慮抽取一次項:
若一次項是結尾項,即沒有常數項存在:

"([+-]?\d*)[xX]$"

若一次項後承接了常數項:

"([+-]?\d*)[xX][^\^]"

將以上的不同冪次項對應的係數都抽取出來,然後執行循環進行計算即可得到所有結果冪次及其對應的係數。

我是基於C++進行編碼的,C++11在C++ Standard Library中引入了正則表達式,引入頭文件regex即可。
下面簡單介紹一下C++11中正則表達式庫的一些使用方法:
一般我們用std::regex_search函數來進行在目標字符串中進行模式字符子串的搜索工作,第一個參數是原字符串,第二個參數爲smatch類型的保存匹配結果的變量,第三個參數爲正則表達式串。找到匹配函數返回true,否則返回false。smatch中第一個元素保存的是匹配到的完整的字符串結果,然後後序存儲的是該完整字符串中被抽取出來的每個部分(即正則表達式中每個括號裏的部分)。
此外另外一個std::regex_match函數與std::regex_search函數的區別是前者是完全匹配,即原字符串中完整的匹配了正則表達式規定的模式串才認爲是一個匹配,而後者只要原字符串中部分的子串實現了匹配即可。
由於std::regex_search可以搜索出字符串中所有的匹配項,因此我們可以實現所有匹配項的一次抽取。具體方法是每次完成了一次匹配後將字符串的首指針移到這個匹配後,再重新進行下次匹配,獲得(如果存在)下一個匹配項。
一個通用的遍歷方式如下:

while (std::regex_search (s,m,e)) 
{
    for (auto x:m) std::cout << x << " ";
    std::cout << std::endl;
    s = m.suffix().str();
}

以下爲完整代碼:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <regex>
#include <cctype>
#include <algorithm>
using namespace std;

int co1[105];
int co2[105];
int ans[105];

int str2int(string str)
{
    if (str == "") return 1;
    if (str == "+") return 1;
    if (str == "-") return -1;
    stringstream stream(str);
    int ans;
    stream >> ans;
    return ans;
}

int solve(string s, int index)
{
    smatch m;
    const regex p1("^\\d+$");//處理純數字
    const regex p2("([+-]+\\d+)$");//處理常數項
    const regex p3("([+-]?\\d*)[xX]$");//處理一次項情況1
    const regex p4("([+-]?\\d*)[xX][^\\^]");//處理一次項情況2
    if (regex_match(s,m,p1))//純數字的情況
    {
        if (index) co2[0] += str2int(m[0]);
        else co1[0] += str2int(m[0]);
        return 0;
    }
    int maxDegree = 0;
    string str = s;
    while (regex_search(str,m,p2))//處理常數項
    {
        if (index) co2[0] += str2int(m[1]);
        else co1[0] += str2int(m[1]);
        str = m.suffix().str();
    }
    str = s;
    while (regex_search(str,m,p3))//處理一次項情況1
    {
        if (index) co2[1] += str2int(m[1]);
        else co1[1] += str2int(m[1]);
        maxDegree = max(maxDegree,1);
        str = m.suffix().str();
    }
    str = s;
    while (regex_search(str,m,p4))//處理一次項情況2
    {
        if (index) co2[1] += str2int(m[1]);
        else co1[1] += str2int(m[1]);
        maxDegree = max(maxDegree,1);
        str = m.suffix().str();
    }
   //const regex pattern("\\+?(-?[1-9]?[0-9]*)[xX]\\^\\+?(-?[1-9]?[0-9]*)");
    const regex pattern("([+-]?\\d*)[xX]\\^([+-]?\\d*)");
    str = s;
    while (regex_search(str,m,pattern))//處理非常數、一次項
    {
        if (index) co2[str2int(m[2])] += str2int(m[1]);
        else co1[str2int(m[2])] += str2int(m[1]);
        maxDegree = max(maxDegree,str2int(m[2]));
        str = m.suffix().str();
    }
    return maxDegree;
}

void test(string s)
{
    smatch m;
  //  const regex p("^\\d+$");//處理純數字
  //  const regex p("([+-]+\\d+)$");//處理常數項
  //  const regex p("([+-]?\\d*)[xX]$");//處理一次項情況1
    const regex p("([+-]?\\d*)[xX][^\\^]");//處理一次項情況2

    bool ok = regex_search(s,m,p);
    cout << "ok: " << ok << endl;
    if (ok)
    {
        cout << "size: " << m.size() << endl;
        cout << "first:" << m[0] << endl;
        cout << "second:" << m[1] << endl;
        cout << "third:" << m[2] << endl;
    }
}
int main()
{
    string str1,str2;
    while (cin >> str1 >> str2)
    {
       // test(str1);
        memset(co1,0,sizeof(co1));
        memset(co2,0,sizeof(co2));
        memset(ans,0,sizeof(ans));
        int maxDegree1 = solve(str1,0);
        int maxDegree2 = solve(str2,1);
        int Max = -1;
        for (int i = 0; i <= maxDegree1; i++)
        {
            for (int j = 0; j <= maxDegree2; j++)
            {
                if (co1[i] == 0 || co2[j] == 0) continue;
                ans[i+j] += co1[i] * co2[j];
                Max = max(Max,i+j);
            }
        }
        int cnt = 0;
        for (int i = Max; i >= 0; i--)
        {
            if (ans[i]) cnt++;
        }
        if (cnt == 0)
        {
            puts("0");
        }
        else
        {
            for (int i = Max; i >= 0; i--)
            {
                if (ans[i])
                {
                    if (cnt == 1)
                    {
                        printf("%d",ans[i]);
                    }
                    else
                    {
                        printf("%d ",ans[i]);
                    }
                    cnt--;
                }
            }
            puts("");
        }
    }
    return 0;
}

愉快的一遍AC了。。
這裏寫圖片描述

|
|
|
|
|
|
|
|
|
|
|
Bonus:由於博主剛入門,相信有很多表達式是可以合併表達的(比如一次項的那兩種情況),如果有更好的合併表達方式還請告訴博主以提高下博主的姿勢水平。。最後附上另一個當年自己寫的解析,居然用一個類來表示每一項。。很面向對象啊??(大霧 反正現在是徹底看不太懂這代碼了…

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char str1[105];
char str2[105];
int num1[55];
int num2[55];
int ans[105];
struct node
{
    int num;
    int pos;
    int degree;
};
node getNum(char str[], int i)
{
    int flag = 0;
    if (str[i] == '-')
    {
        flag = 1;
        i++;
    }
    else if (str[i] == '+')
    {
        flag = 2;
        i++;
    }
    int ans = 0;
    node a;
    for (; i < strlen(str); i++)
    {
        int num = str[i] - '0';
        if (!(num >= 0 && num <= 9)) break;
        ans = ans * 10 + num;
    }
    if (i == strlen(str))
    {
        node a;
        a.num = ans;
        if (flag == 1)
            a.num = -a.num;
        a.degree = 0;//const degree
        a.pos = i;
        return a;
    }
    i++;
    if (str[i] == '^')
    {
        int sum = 0;
        while (true)
        {
            i++;
            int num = str[i] - '0';
            if (!(num >= 0 && num <= 9)) break;
            sum = sum * 10 + num;
        }
        i--;
        a.degree = sum;
    }
    else
    {
        a.degree = 1;
        i--;
    }
    if (ans == 0) ans = 1;
    a.num = ans;
    a.pos = i;
    if (flag == 1) a.num = -a.num;
    return a;
}

int getInfo(char str[],int num[])
{
    int maxDegree = 0;
    int i = 0;
    size_t len = strlen(str);
    while (i < len)
    {
        node a = getNum(str, i);
        maxDegree = max(maxDegree,a.degree);
        num[a.degree] = a.num;
        i = a.pos;
        i++;
    }
    return maxDegree;
}
int main()
{
    while (~scanf("%s", str1))
    {
        memset(ans,0,sizeof(ans));
        memset(num1,0,sizeof(num1));
        memset(num2,0,sizeof(num2));
        int maxDegree1 = 0;
        int maxDegree2 = 0;
        int Max = 0;
        scanf("%s", str2);
        maxDegree1 = getInfo(str1,num1);
        maxDegree2 = getInfo(str2,num2);
        for (int i = 0; i <= maxDegree1; i++)
        {
            for (int j = 0; j <= maxDegree2; j++)
            {
                if (num1[i] == 0 || num2[j] == 0) continue;
                ans[i+j] += num1[i] * num2[j];
                Max = max(Max,i+j);
            }
        }
        int cnt = 0;
        for (int i = Max; i >= 0; i--)
        {
            if (ans[i]) cnt++;
        }
        if (cnt == 0)
        {
            puts("0");
        }
        else
        {
            for (int i = Max; i >= 0; i--)
            {
                if (ans[i])
                {
                    if (cnt == 1)
                    {
                        printf("%d",ans[i]);
                    }
                    else
                    {
                        printf("%d ",ans[i]);
                    }
                    cnt--;
                }
            }
            puts("");
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章