NOIP 2005 等价表达式(hash算法)

17 篇文章 0 订阅
11 篇文章 0 订阅

题目描述  Description

明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。

这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?

这个选择题中的每个表达式都满足下面的性质:
1
表达式只可能包含一个变量a
2
表达式中出现的数都是正整数,而且都小于10000
3
表达式中可以包括四种运算+(加),-(减),*(乘),^(乘幂),以及小括号()。小括号的优先级最高,其次是^,然后是*,最后是+-+-的优先级是相同的。相同优先级的运算从左到右进行。(注意:运算符+-*^以及小括号()都是英文字符)
4
幂指数只可能是110之间的正整数(包括110)。
5
表达式内部,头部或者尾部都可能有一些多余的空格。
下面是一些合理的表达式的例子:
((a^1)^2)^3
a*a+a-a((a+a))9999+(a-a)*a1+(a-1)^31^10^9……

输入描述 Input Description

输入第一行给出的是题干中的表达式。第二行是一个整数n2<=n<=26),表示选项的个数。后面n行,每行包括一个选项中的表达式。这n个选项的标号分别是ABCD……

输入中的表达式的长度都不超过50个字符,而且保证选项中总有表达式和题干中的表达式是等价的。

输出描述 Output Description

输出包括一行,这一行包括一系列选项的标号,表示哪些选项是和题干中的表达式等价的。选项的标号按照字母顺序排列,而且之间没有空格。

样例输入 Sample Input

(a+1)^2
3
(a-1)^2+4*a
a+1+a
a^2+2*a*1+1^2+10-10+a-a

样例输出 Sample Output

AC

数据范围及提示 Data Size & Hint

【数据规模】
对于30%的数据,表达式中只可能出现两种运算符+-
对于其它的数据,四种运算符+-*^在表达式中都可能出现。
对于全部的数据,表达式中都可能出现小括号()

此题算是hash算法的一道基础题,但是涉及几个小知识点,所以在此列出。

思路:

看到此题首先想到就是表达式求值,但是发现有个变量a,如果我们用一个常数代替a不就变成表达式求值了,另一个问题就是求出的答案太大,不易保存,而且耗费空间不知几何,按照hash算法的思想,将表达式的值MOD一个值(素数),这样计算就简便很多,问题又来了,我们知道hash是会出现冲突,如果只用一组数据计算那么冲突的概率就很大,解决这问题就很简单了,我们多使用几个常数计算,如果几个常数求出的答案都一样我们就直接判定相等的表达式(虽可能还是有冲突,但是概率很小)。

细节:

此题的表达式并不一定合法,所以首先需要去掉多余的空格,把不匹配的括号也去掉,存储数据注意是否需要用到longlong,算指数时可以考虑使用快速幂,表达式求值使用表达式树更为简单一些。

题目链接:http://codevs.cn/problem/1107/


#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <utility>
#include <map>
#include <set>
#include <queue>
#include <vector>
#include <iostream>
#include <stack>
#include <sstream>
using namespace std;
#define INF 0x3f3f3f3f
#define eps 1e-6
#define CLR( a, v ) memset ( a, v, sizeof ( a ) )
#define LL long long
#define DBUG printf ( "here!!!\n" )
#define rep( i, a, b ) for ( int i = ( a ); i < ( b ); i ++ )
#define PB push_back
#define ULL unsigned long long
#define PI acos ( -1.0 )
#define lson l, m, rt << 1
#define rson m+1, r, rt << 1 | 1
#define lowbit( x ) ( ( x )&( -x ) )
#define CASE int Test; scanf ( "%d", &Test ); for ( int cas = 1; cas <= Test; cas ++ )
#define ALL( x ) x.begin ( ), x.end ( )
#define INS( x ) inserter ( x, x.begin ( ) )
typedef pair < int, int > Pii;
typedef pair < double, double > Pdd;
typedef set < int > Set;
const int maxn = 105;
int read_int ( )
{
    int res = 0, f = 1;
    int ch = getchar ( );
    while ( ch < '0' || ch > '9' )
    {
        if ( ch == -1 )
            return -1;
        if ( ch == '-' )
            f = -1;
        ch = getchar ( );
    }
    while ( ch >= '0' && ch <= '9' )
    {
        res = res*10+( ch-'0' );
        ch = getchar ( );
    }
    return res*f;
}
char str[maxn*maxn], ch[maxn][maxn], res[maxn];
int L[maxn], R[maxn], rt;
bool del[maxn*maxn];
LL ans[5];
LL H[5] = { 107, 1009, 117, 119, 1211 };
LL MOD[5] = { 1000003, 1000007, 1000009, 10000007, 1111107 };
void to_space ( char * s )
{
    int cnt = 0;    //去掉多余空格
    for ( int i = 0; s[i]; i ++ )
        if ( s[i] != ' ' )
            s[cnt ++] = s[i];
    s[cnt] = '\0';
}
bool isDigit ( int l, int r, char * s )
{
    for ( int i = l; i < r; i ++ )
        if ( ! ( s[i] >= '0' && s[i] <= '9' || s[i] == 'a' ) )
            return false;
    return true;
}
int Build_Tree ( int l, int r, char * s )
{
    if ( l > r )    //建表达式树
        return 0;
    if ( isDigit ( l, r, s ) )
    {
        int u = rt ++;
        for ( int i = l; i < r; i ++ )
            ch[u][i-l] = s[i];
        ch[u][r-l] = '\0';
        return u;
    }
    int p1, p2, p3, c = 0;
    p1 = p2 = p3 = -1;
    for ( int i = l; i < r; i ++ )
    {
        switch ( s[i] )
        {
            case '(' : c --; break;
            case ')' : c ++; break;
            case '^' :
                if ( c == 0 )
                    p1 = i;
            break;
            case '*' :
                if ( c == 0 )
                    p2 = i;
            break;
            case '+' : case '-' :
                if ( c == 0 )
                    p3 = i;
            break;
        }
    }
    if ( p2 == -1 )
        p2 = p1;
    if ( p3 == -1 )
        p3 = p2;
    if ( p3 == -1 )
        return Build_Tree ( l+1, r-1, s );
    int u = rt ++;
    ch[u][0] = s[p3];
    ch[u][1] = '\0';
    L[u] = Build_Tree ( l, p3, s );
    R[u] = Build_Tree ( p3+1, r, s );
    return u;
}
stack < LL > S; //用LL保存数据
LL toInt ( char * s )
{
    LL res = 0;
    for ( int i = 0; s[i]; i ++ )
        res = res*10+( s[i]-'0' );
    return res;
}
LL quick_Mod ( LL a, LL b, LL M )   //快速幂
{
    LL res = 1;
    while ( b > 0 )
    {
        if ( b&1 )
            res = res*a%M;
        a = a*a%M;
        b >>= 1;
    }
    return res;
}
LL oper ( LL a, LL b, char c, LL M )
{
    switch ( c )    //运算
    {
        case '-' :
            return ( ( a-b )%M+M )%M;
        case '+' :
            return ( a+b )%M;
        case '*' :
            return a*b%M;
        case '^' :
            return quick_Mod ( a, b, M );
    }
    return 0;
}
void dfs ( int u, LL x, LL M )
{
    if ( L[u] ) //转成后缀表达式求值
        dfs ( L[u], x, M );
    if ( R[u] )
        dfs ( R[u], x, M );
    //printf ( "%s ", ch[u] );
    if ( ch[u][0] >= '0' && ch[u][0] <= '9' || ch[u][0] == 'a' )
    {
        if ( ch[u][0] == 'a' )
            S.push ( x );
        else
            S.push ( toInt ( ch[u] ) );
    }
    else
    {
        LL b = S.top ( );
        S.pop ( );
        LL a = S.top ( );
        S.pop ( );
        S.push ( oper ( a, b, ch[u][0], M ) );
    }
}
void delBr ( char * s )
{
    CLR ( del, false ); //删除多余括号
    int c = 0;
    for ( int i = 0; s[i]; i ++ )
    {
        if ( s[i] == '(' )
            c --;
        if ( s[i] == ')' )
            c ++;
        if ( c > 0 )
            del[i] = true, c = 0;
    }
    int p = 0;
    for ( int i = 0; s[i]; i ++ )
        if ( del[i] == false )
            s[p ++] = s[i];
    s[p] = '\0';
    c = 0;
    CLR ( del, false );
    for ( int i = p-1; i >= 0; i -- )
    {
        if ( s[i] == ')' )
            c --;
        if ( s[i] == '(' )
            c ++;
        if ( c > 0 )
            del[i] = true, c = 0;
    }
    p = 0;
    for ( int i = 0; s[i]; i ++ )
        if ( del[i] == false )
            s[p ++] = s[i];
    s[p] = '\0';
}
void solve ( )
{
    int n;
    gets ( str );
    to_space ( str );
    delBr ( str );
    rt = 1;
    Build_Tree ( 0, strlen ( str ), str );
    for ( int i = 0; i < 5; i ++ )
    {
        while ( ! S.empty ( ) )
            S.pop ( );
        dfs ( 1, H[i], MOD[i] );
        ans[i] = S.top ( );
        //printf ( "%lld ", ans[i] );
        //printf ( "\n" );
    }
    scanf ( "%d", &n );
    getchar ( );
    int p = 0;
    for ( int i = 0; i < n; i ++ )
    {
        gets ( str );
        to_space ( str );
        delBr ( str );
        rt = 1;
        CLR ( L, 0 );
        CLR ( R, 0 );
        Build_Tree ( 0, strlen ( str ), str );
        bool ok = true;
        for ( int j = 0; j < 5; j ++ )
        {
            while ( ! S.empty ( ) )
                S.pop ( );
            dfs ( 1, H[j], MOD[j] );
            //printf ( "%lld ", S.top ( ) );
            if ( S.top ( ) != ans[j] )
            {
                ok = false;
                break ;
            }
        }
        //printf ( "\n" );
        if ( ok )
            res[p ++] = 'A'+i;
    }
    res[p] = '\0';
    puts ( res );
}
int main ( )
{
    solve ( );
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值