This documentation is automatically generated by online-judge-tools/verification-helper
#define PROBLEM "https://yukicoder.me/problems/no/1695"
#include "../../string/manacher.hpp"
#include "../shortest_path.hpp"
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
constexpr int INF = 1 << 28;
int solve(const string &S, const string &T) {
int nmatch = 0;
while (nmatch < min<int>(S.size(), T.size()) and S[nmatch] == T[nmatch]) nmatch++;
if (!nmatch) return INF;
if (T.size() % 2) return INF;
auto trev = T;
if (trev != T) return INF;
shortest_path<int> graph(T.size() + 1);
for (int i = 0; i < int(T.size()); ++i) graph.add_edge(i, i + 1, 0);
auto ps = enumerate_palindromes(T);
for (const auto &p : ps) {
auto l = p.first, r = p.second;
if ((l + r) % 2 == 0) graph.add_edge(r, (l + r) / 2, 1);
}
graph.zero_one_bfs(T.size(), nmatch);
return graph.dist[nmatch];
}
int main() {
cin.tie(nullptr), ios::sync_with_stdio(false);
int N, M;
string S, T;
cin >> N >> M >> S >> T;
int ret = solve(S, T);
reverse(S.begin(), S.end());
ret = min(ret, solve(S, T));
cout << (ret < INF ? ret : -1) << '\n';
}
#line 1 "graph/test/zero_one_bfs.yuki1695.test.cpp"
#define PROBLEM "https://yukicoder.me/problems/no/1695"
#line 2 "string/manacher.hpp"
#include <string>
#include <utility>
#include <vector>
// CUT begin
// Manacher's Algorithm: radius of palindromes
// Input: std::string or std::vector<T> of length N
// Output: std::vector<int> of size N
// Complexity: O(N)
// Sample:
// - `sakanakanandaka` -> [1, 1, 2, 1, 4, 1, 4, 1, 2, 2, 1, 1, 1, 2, 1]
// Reference: https://snuke.hatenablog.com/entry/2014/12/02/235837
template <typename T> std::vector<int> manacher(const std::vector<T> &S) {
std::vector<int> res(S.size());
int i = 0, j = 0;
while (i < int(S.size())) {
while (i - j >= 0 and i + j < int(S.size()) and S[i - j] == S[i + j]) j++;
res[i] = j;
int k = 1;
while (i - k >= 0 and i + k < int(S.size()) and k + res[i - k] < j)
res[i + k] = res[i - k], k++;
i += k, j -= k;
}
return res;
}
std::vector<int> manacher(const std::string &S) {
std::vector<char> v(S.size());
for (int i = 0; i < int(S.size()); i++) v[i] = S[i];
return manacher(v);
}
template <typename T>
std::vector<std::pair<int, int>> enumerate_palindromes(const std::vector<T> &vec) {
std::vector<T> v;
const int N = vec.size();
for (int i = 0; i < N - 1; i++) {
v.push_back(vec[i]);
v.push_back(-1);
}
v.push_back(vec.back());
const auto man = manacher(v);
std::vector<std::pair<int, int>> ret;
for (int i = 0; i < N * 2 - 1; i++) {
if (i & 1) {
int w = man[i] / 2;
ret.emplace_back((i + 1) / 2 - w, (i + 1) / 2 + w);
} else {
int w = (man[i] - 1) / 2;
ret.emplace_back(i / 2 - w, i / 2 + w + 1);
}
}
return ret;
}
std::vector<std::pair<int, int>> enumerate_palindromes(const std::string &S) {
std::vector<char> v(S.size());
for (int i = 0; i < int(S.size()); i++) v[i] = S[i];
return enumerate_palindromes<char>(v);
}
#line 2 "graph/shortest_path.hpp"
#include <algorithm>
#include <cassert>
#include <deque>
#include <fstream>
#include <functional>
#include <limits>
#include <queue>
#line 10 "graph/shortest_path.hpp"
#include <tuple>
#line 13 "graph/shortest_path.hpp"
template <typename T, T INF = std::numeric_limits<T>::max() / 2, int INVALID = -1>
struct shortest_path {
int V, E;
bool single_positive_weight;
T wmin, wmax;
std::vector<std::pair<int, T>> tos;
std::vector<int> head;
std::vector<std::tuple<int, int, T>> edges;
void build_() {
if (int(tos.size()) == E and int(head.size()) == V + 1) return;
tos.resize(E);
head.assign(V + 1, 0);
for (const auto &e : edges) ++head[std::get<0>(e) + 1];
for (int i = 0; i < V; ++i) head[i + 1] += head[i];
auto cur = head;
for (const auto &e : edges) {
tos[cur[std::get<0>(e)]++] = std::make_pair(std::get<1>(e), std::get<2>(e));
}
}
shortest_path(int V = 0) : V(V), E(0), single_positive_weight(true), wmin(0), wmax(0) {}
void add_edge(int s, int t, T w) {
assert(0 <= s and s < V);
assert(0 <= t and t < V);
edges.emplace_back(s, t, w);
++E;
if (w > 0 and wmax > 0 and wmax != w) single_positive_weight = false;
wmin = std::min(wmin, w);
wmax = std::max(wmax, w);
}
void add_bi_edge(int u, int v, T w) {
add_edge(u, v, w);
add_edge(v, u, w);
}
std::vector<T> dist;
std::vector<int> prev;
// Dijkstra algorithm
// - Requirement: wmin >= 0
// - Complexity: O(E log E)
using Pque = std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>,
std::greater<std::pair<T, int>>>;
template <class Heap = Pque> void dijkstra(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF);
prev.assign(V, INVALID);
dist[s] = 0;
Heap pq;
pq.emplace(0, s);
while (!pq.empty()) {
T d;
int v;
std::tie(d, v) = pq.top();
pq.pop();
if (t == v) return;
if (dist[v] < d) continue;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = d + nx.second;
if (dist[nx.first] > dnx) {
dist[nx.first] = dnx, prev[nx.first] = v;
pq.emplace(dnx, nx.first);
}
}
}
}
// Dijkstra algorithm
// - Requirement: wmin >= 0
// - Complexity: O(V^2 + E)
void dijkstra_vquad(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF);
prev.assign(V, INVALID);
dist[s] = 0;
std::vector<char> fixed(V, false);
while (true) {
int r = INVALID;
T dr = INF;
for (int i = 0; i < V; i++) {
if (!fixed[i] and dist[i] < dr) r = i, dr = dist[i];
}
if (r == INVALID or r == t) break;
fixed[r] = true;
int nxt;
T dx;
for (int e = head[r]; e < head[r + 1]; ++e) {
std::tie(nxt, dx) = tos[e];
if (dist[nxt] > dist[r] + dx) dist[nxt] = dist[r] + dx, prev[nxt] = r;
}
}
}
// Bellman-Ford algorithm
// - Requirement: no negative loop
// - Complexity: O(VE)
bool bellman_ford(int s, int nb_loop) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
for (int l = 0; l < nb_loop; l++) {
bool upd = false;
for (int v = 0; v < V; v++) {
if (dist[v] == INF) continue;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[v] + nx.second;
if (dist[nx.first] > dnx) dist[nx.first] = dnx, prev[nx.first] = v, upd = true;
}
}
if (!upd) return true;
}
return false;
}
// Bellman-ford algorithm using deque
// - Requirement: no negative loop
// - Complexity: O(VE)
void spfa(int s) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF);
prev.assign(V, INVALID);
dist[s] = 0;
std::deque<int> q;
std::vector<char> in_queue(V);
q.push_back(s), in_queue[s] = 1;
while (!q.empty()) {
int now = q.front();
q.pop_front(), in_queue[now] = 0;
for (int e = head[now]; e < head[now + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[now] + nx.second;
int nxt = nx.first;
if (dist[nxt] > dnx) {
dist[nxt] = dnx;
if (!in_queue[nxt]) {
if (q.size() and dnx < dist[q.front()]) { // Small label first optimization
q.push_front(nxt);
} else {
q.push_back(nxt);
}
prev[nxt] = now, in_queue[nxt] = 1;
}
}
}
}
}
// 01-BFS
// - Requirement: all weights must be 0 or w (positive constant).
// - Complexity: O(V + E)
void zero_one_bfs(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
std::vector<int> q(V * 4);
int ql = V * 2, qr = V * 2;
q[qr++] = s;
while (ql < qr) {
int v = q[ql++];
if (v == t) return;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[v] + nx.second;
if (dist[nx.first] > dnx) {
dist[nx.first] = dnx, prev[nx.first] = v;
if (nx.second) {
q[qr++] = nx.first;
} else {
q[--ql] = nx.first;
}
}
}
}
}
// Dial's algorithm
// - Requirement: wmin >= 0
// - Complexity: O(wmax * V + E)
void dial(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
std::vector<std::vector<std::pair<int, T>>> q(wmax + 1);
q[0].emplace_back(s, dist[s]);
int ninq = 1;
int cur = 0;
T dcur = 0;
for (; ninq; ++cur, ++dcur) {
if (cur == wmax + 1) cur = 0;
while (!q[cur].empty()) {
int v = q[cur].back().first;
T dnow = q[cur].back().second;
q[cur].pop_back(), --ninq;
if (v == t) return;
if (dist[v] < dnow) continue;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[v] + nx.second;
if (dist[nx.first] > dnx) {
dist[nx.first] = dnx, prev[nx.first] = v;
int nxtcur = cur + int(nx.second);
if (nxtcur >= int(q.size())) nxtcur -= q.size();
q[nxtcur].emplace_back(nx.first, dnx), ++ninq;
}
}
}
}
}
// Solver for DAG
// - Requirement: graph is DAG
// - Complexity: O(V + E)
bool dag_solver(int s) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
std::vector<int> indeg(V, 0);
std::vector<int> q(V * 2);
int ql = 0, qr = 0;
q[qr++] = s;
while (ql < qr) {
int now = q[ql++];
for (int e = head[now]; e < head[now + 1]; ++e) {
const auto &nx = tos[e];
++indeg[nx.first];
if (indeg[nx.first] == 1) q[qr++] = nx.first;
}
}
ql = qr = 0;
q[qr++] = s;
while (ql < qr) {
int now = q[ql++];
for (int e = head[now]; e < head[now + 1]; ++e) {
const auto &nx = tos[e];
--indeg[nx.first];
if (dist[nx.first] > dist[now] + nx.second)
dist[nx.first] = dist[now] + nx.second, prev[nx.first] = now;
if (indeg[nx.first] == 0) q[qr++] = nx.first;
}
}
return *max_element(indeg.begin(), indeg.end()) == 0;
}
// Retrieve a sequence of vertex ids that represents shortest path [s, ..., goal]
// If not reachable to goal, return {}
std::vector<int> retrieve_path(int goal) const {
assert(int(prev.size()) == V);
assert(0 <= goal and goal < V);
if (dist[goal] == INF) return {};
std::vector<int> ret{goal};
while (prev[goal] != INVALID) {
goal = prev[goal];
ret.push_back(goal);
}
std::reverse(ret.begin(), ret.end());
return ret;
}
void solve(int s, int t = INVALID) {
if (wmin >= 0) {
if (single_positive_weight) {
zero_one_bfs(s, t);
} else if (wmax <= 10) {
dial(s, t);
} else {
if ((long long)V * V < (E << 4)) {
dijkstra_vquad(s, t);
} else {
dijkstra(s, t);
}
}
} else {
bellman_ford(s, V);
}
}
// Warshall-Floyd algorithm
// - Requirement: no negative loop
// - Complexity: O(E + V^3)
std::vector<std::vector<T>> floyd_warshall() {
build_();
std::vector<std::vector<T>> dist2d(V, std::vector<T>(V, INF));
for (int i = 0; i < V; i++) {
dist2d[i][i] = 0;
for (const auto &e : edges) {
int s = std::get<0>(e), t = std::get<1>(e);
dist2d[s][t] = std::min(dist2d[s][t], std::get<2>(e));
}
}
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
if (dist2d[i][k] == INF) continue;
for (int j = 0; j < V; j++) {
if (dist2d[k][j] == INF) continue;
dist2d[i][j] = std::min(dist2d[i][j], dist2d[i][k] + dist2d[k][j]);
}
}
}
return dist2d;
}
void to_dot(std::string filename = "shortest_path") const {
std::ofstream ss(filename + ".DOT");
ss << "digraph{\n";
build_();
for (int i = 0; i < V; i++) {
for (int e = head[i]; e < head[i + 1]; ++e) {
ss << i << "->" << tos[e].first << "[label=" << tos[e].second << "];\n";
}
}
ss << "}\n";
ss.close();
return;
}
};
#line 5 "graph/test/zero_one_bfs.yuki1695.test.cpp"
#include <iostream>
#line 7 "graph/test/zero_one_bfs.yuki1695.test.cpp"
using namespace std;
constexpr int INF = 1 << 28;
int solve(const string &S, const string &T) {
int nmatch = 0;
while (nmatch < min<int>(S.size(), T.size()) and S[nmatch] == T[nmatch]) nmatch++;
if (!nmatch) return INF;
if (T.size() % 2) return INF;
auto trev = T;
if (trev != T) return INF;
shortest_path<int> graph(T.size() + 1);
for (int i = 0; i < int(T.size()); ++i) graph.add_edge(i, i + 1, 0);
auto ps = enumerate_palindromes(T);
for (const auto &p : ps) {
auto l = p.first, r = p.second;
if ((l + r) % 2 == 0) graph.add_edge(r, (l + r) / 2, 1);
}
graph.zero_one_bfs(T.size(), nmatch);
return graph.dist[nmatch];
}
int main() {
cin.tie(nullptr), ios::sync_with_stdio(false);
int N, M;
string S, T;
cin >> N >> M >> S >> T;
int ret = solve(S, T);
reverse(S.begin(), S.end());
ret = min(ret, solve(S, T));
cout << (ret < INF ? ret : -1) << '\n';
}