URAL - 1486 Equal Squares 哈希、二维hash、二分、卡大素数

Equal Squares

  URAL - 1486


During a discussion of problems at the Petrozavodsk Training Camp, Vova and Sasha argued about who of them could in 300 minutes find a pair of equal squares of the maximal size in a matrix of size  N ×   M containing lowercase English letters. Squares could overlap each other but could not coincide. He who had found a pair of greater size won. Petr walked by, looked at the matrix, said that the optimal pair of squares had sides  K, and walked on. Vova and Sasha still cannot find this pair. Can you help them?
Input
The first line contains integers  N and  M separated with a space. 1 ≤   NM ≤ 500. In the next  N lines there is a matrix consisting of lowercase English letters,  M symbols per line.
Output
In the first line, output the integer  K which Petr said. In the next two lines, give coordinates of upper left corners of maximal equal squares. If there exist more than one pair of equal squares of size  K, than you may output any of them. The upper left cell of the matrix has coordinates (1, 1), and the lower right cell has coordinates (  N,   M). If there are no equal squares in the matrix, then output 0.
Example
input output
5 10
ljkfghdfas
isdfjksiye
pgljkijlgp
eyisdafdsi
lnpglkfkjl
3
1 1
3 3

Source URAL - 1486
My Solution
题意:给出n个长度为m的字符串(n,m <= 500),然后问选取一个最大的正方形,使得有2个正方形区域的字符串是相同的,求出这个最大正方形的边长即两次出现位置的正方形左上角坐标。 哈希、二维hash、二分、卡大素数
题目直接就暗示着用哈希来做 ^_^, 然后可以把每个字符串分别用同一个大素数hash掉, 然后二分边长, check的时候,检查每个长度为mid的正方形是否再次出现, 检查的时候再去另一个大素数pt,把横向的第一次哈希得到的一段字符串的哈希值作为一个元素,把这些元素再次进行哈希, 然后在纵向的求长度为mid的,哈希值是否再次出现。 然后卡大素数了,所以把1e12+9改成了 191919191919。 复杂度 O(n*n*logn)
#include <iostream>
#include <cstdio>
#include <string>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int, int> ii;
typedef unsigned long long ull;
const int MAXN = 5e2 + 8;
const ull p = 1e12 + 7, pt = 191919191919;

struct ha{
    ull ha[MAXN][MAXN], xp[MAXN], sz;
    void init1(int n){
        sz = n;
        xp[0] = 1;
        for(int i = 1; i <= sz; i++) xp[i] = xp[i-1] * p;
    }
    void init2(int id, const string &str){//0~n-1 based
        ha[id][sz] = 0;
        for(int i = sz - 1; i >= 0; i--){
            ha[id][i] = ha[id][i+1] * p + (str[i] - 'a' + 1);
        }
    }
    ull get_ha(int id, int st, int len){
        return ha[id][st] - ha[id][st + len] * xp[len];
    }

}Hash;

string s[MAXN];
map<ull, ii> mp;
int n, m, ansk = 0;
ii ans1, ans2;
ull hat[MAXN], xpt[MAXN];
inline void initxpt(int sz)
{
    xpt[0] = 1;
    for(int i = 1; i <= sz; i++) xpt[i] = xpt[i-1] * pt;
}

inline bool check(int x)
{
    mp.clear();
    int i, j, k;
    ull t;
    for(j = 0; j + x - 1 < m; j++){
        hat[0] = 0;
        for(i = 1; i <= n; i++){
            hat[i] = hat[i-1]*pt + Hash.get_ha(i, j, x);
        }
        for(k = x; k <= n; k++){
            t = hat[k] - hat[k - x] * xpt[x];
            if(mp.find(t) != mp.end()){
                ans1 = mp[t];
                ans2 = ii(k - x + 1, j + 1);
                return true;
            }
            else{
                mp[t] = ii(k - x + 1, j + 1);
            }
        }
    }
    return false;
}


int main()
{
    #ifdef LOCAL
    freopen("17.in", "r", stdin);
    //freopen("17.out", "w", stdout);

    #endif // LOCAL
    ios::sync_with_stdio(false); cin.tie(0);

    cin >> n >> m;
    Hash.init1(m);
    for(int i = 1; i <= n; i++){
        cin >> s[i];
        Hash.init2(i, s[i]);
    }
    initxpt(n);
    int l = 0, r = min(n, m) + 1, mid;
    while(l + 1 < r){
        mid = (l + r) >> 1;
        if(check(mid)){
            ansk = mid; l = mid;
        }
        else r = mid;
    }
    if(ansk) cout << ansk << "\n" << ans1.first << " " << ans1.second << "\n" << ans2.first << " " << ans2.second << endl;
    else cout << ansk << "\n";
    return 0;
}

Thank you!                                                                                                                                              ------from  ProLights

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值