利用正则表达式求解多项式相乘问题

最近几天在学习正则表达式,然后想起来以前一些关于字符串解析的问题都可以利用正则表达式进行求解,而不必人工、机械的去自己一位位去解析目标字符串。比如经典的多项式相乘问题,我们来看一个问题的实例:
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;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值