Data su dva faјla, dimenziјe \(n \times n\) i \(m \times m\), respektivno. Prvi faјl јe originalna slika zadata ascii karakterima. Drugi faјl predstavљa pokušaј kopiraњa dela originalne slike. Proveriti da li јe pokušaј kopiraњa uspeo.
Sa standardnog ulaza se unosi prvo broј \(n\) (\(1 \leq n \leq 200\)), zatim broј \(m\) (\(m \leq n\)). Nakon toga se unosi matrica karaktera dimeziјe \(n \times n\), a na kraјu matrica karaktera dimenziјe \(m \times m\).
Ukoliko јe druga matrica sadržana u prvoј na standardni izlaz
ispisati da
, a inače ne
.
0
0
18 4 ~.____......_....~ ~|.._.\..../.\...~ ~|.|_).|../._.\..~ ~|.._.<../.___.\.~ ~|_|_\_\/_/_..\_\~ ~|.__.)_._|.\.|.|~ ~|.._.\|.||..\|.|~ ~|.|_).|.||.|\..|~ ~|____/___|_|.\_|~ ~|.|/./.../.\....~ ~|.'./.../._.\...~ ~|...\../.___.\..~ ~|_|\_\/_/__.\_\.~ ~|.._.\|.._.\....~ ~|.|_).|.|_).|...~ ~|.._.<|..__/....~ ~|_|.\_\_|.......~ ~................~ _._| |.|| |.|| ___|
da
19 4 ################### #.................# #.................# #._..._......_....# #|.\.|.|..../.\...# #|..\|.|.../._.\..# #|.|\..|../.___.\.# #|_|_\_|_/_/.._\_\# #|_._|.\.\..././..# #.|.|...\.\././...# #.|.|....\.V./....# #|___|_...\_/.....# #|.____|..........# #|.._|............# #|.|___...........# #|_____|..........# #.................# #.................# ################### nnnn nnnn nnnn nnnn
ne
17 3 ................. ................. ................. ...(.)_____(.)... ..../.O...O.\.... ...|...(@)...|... ...\....~..../... ....\.<}*{>./.... .___/..___..\___. ()___./...\.___() ....(.\___/.).... .../_./...\._\... ..(__).....(__).. ................. ................. ................. ................. (@) .~. }*{
da
Za svaki pomeraj \((s, l) \in \{(i, j) : 1 \leq 1 \leq n - m + 1, 1 \leq j \leq n - m + 1\}\) proverimo da li \(P[1..m, 1..m] = T[s+1..s+m, l+1..s+m]\). Složenost jedne provere \(P[1..m, 1..m] = T[s+1..s+m, l+1..s+m]\) je \(O(m^2)\), a kako imamo ukupno \((n - m + 1)^2\) ovakvih provera, složenost algoritma je \(O((nm)^2)\).
#include <iostream>
#include <vector>
using namespace std;
bool check(const vector<vector<char>> &text,
const vector<vector<char>> &pattern,
const int s, const int l)
{
const int m = pattern.size();
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
if (text[s + i][l + j] != pattern[i][j]) {
return false;
}
}
}
return true;
}
bool naive_matcher(const vector<vector<char>> &text,
const vector<vector<char>> &pattern)
{
const int n = text.size();
const int m = pattern.size();
for (int s = 0; s < n - m + 1; s++) {
for (int l = 0; l < n - m + 1; l++) {
if (check(text, pattern, s, l)) {
return true;
}
}
}
return false;
}
int main(void)
{
int n, m;
std::cin >> n >> m;
<vector<char>> text(n, vector<char> (n));
vector<vector<char>> pattern(m, vector<char> (m));
vector
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
>> text[i][j];
cin }
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
>> pattern[i][j];
cin }
}
if (naive_matcher(text, pattern)) {
<< "da" << endl;
cout } else {
<< "ne" << endl;
cout }
return 0;
}
Bolje rešenje se dobija primenom generalizacije Rabin-Karpovog algoritma na dve dimenzije. U originalnom algoritmu, računamo heš vrednost niske i heš vrednosti segmenata teksta koje onda upoređujemo. U ovom zadatku, računaćemo haš vrednost \(p\) matrice \(P[1..m, 1..m]\) i heš vrednosti \(t_{s,l}\) segmenata \(T[s+1..s+m, l+1..l+m]\). Kao i u originalnom algortmu, ako \(p \neq t_{s, l}\), onda sigurno \(P[1..m, 1..m] \neq T[s+1..s+m, l+1..l+m]\), a ako \(p = t_{s, l}\), onda moramo proveriti da li \(P[1..m, 1..m] = T[s+1..s+m, l+1..l+m]\) (ovu proveru izvršavamo kao i u naivnom algorimu i ona je složenosti \(O(m^2)\)).
Dalje, diskutujemo o tome kako izračunati haš vrednosti \(p\) i \(t_{s, 0}\), kao i način na koji možemo izračunati \(t_{s, l+1}\) ukoliko znamo \(t_{s, l}\). Heš vrednost \(p\) možemo dobiti tako što najpre izračunamo heš vrednosti kolona matrice \(P[1..m, 1..m]\), a zatim računamo heš vrednost tih heš vrednosti.
#include <iostream>
#include <vector>
using namespace std;
long long q = 257;
long long d = 256;
// 0 <= a mod n <= n - 1
long long mod(const long long a, const long long n)
{
if (a > 0) return a % n;
return (a % n + n) % n;
}
// a^b mod n
long long pow_mod(long long a, long long b, const long long n)
{
int d = 1;
= mod(a, n);
a while (b > 0) {
if (b & 1) {
= mod(d * a, n);
d }
>>= 1;
b = mod(a * a, n);
a }
return d;
}
bool check(const vector<vector<char>> &text,
const vector<vector<char>> &pattern,
int s, int l)
{
const int m = pattern.size();
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
if (text[i + s][j + l] != pattern[i][j]) {
return false;
}
}
}
return true;
}
<long long> hash_strips(const vector<vector<char>> &map, const int m)
vector{
const int n = map.size();
<long long> h (n, 0);
vector
for (int j = 0; j < n; j++) {
for (int i = 0; i < m; i++) {
[j] = mod(mod(d * h[j], q) + map[i][j], q);
h}
}
return h;
}
long long hash_value(const vector<long long> hash, const int m)
{
long long val = 0;
for (int j = 0; j < m; j++) {
= mod(d * val + hash[j], q);
val }
return val;
}
bool rabin_karp(const vector<vector<char>> &text,
const vector<vector<char>> &pattern)
{
const long long n = text.size();
const long long m = pattern.size();
const long long h = pow_mod(d, m - 1, q);
auto t_hash = hash_strips(text, m);
auto p_hash = hash_strips(pattern, m);
long long p_val = hash_value(p_hash, m);
for (int s = 0; s < n - m + 1; s++) {
long long t_val = hash_value(t_hash, m);
for (int l = 0; l < n - m + 1; l++) {
if (p_val == t_val && check(text, pattern, s, l)) {
return true;
}
if (l < n - m) { // horizontal roll
= mod(d * (t_val - h * t_hash[l]) + t_hash[l + m], q);
t_val }
}
if (s < n - m) { // vertical roll
for (int j = 0; j < n; j++) {
[j] = mod(d * (t_hash[j] - h * text[s][j]) + text[s + m][j], q);
t_hash}
}
}
return false;
}
int main(void)
{
int n, m;
>> n >> m;
cin
<vector<char>> text(n, vector<char> (n));
vector<vector<char>> pattern(m, vector<char> (m));
vector
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
>> text[i][j];
cin }
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
>> pattern[i][j];
cin }
}
if (rabin_karp(text, pattern)) {
<< "da" << endl;
cout } else {
<< "ne" << endl;
cout }
return 0;
}