#include<iostream>
#include<vector>
#include<list>
#include<map>
#include<set>
#include<functional>
#include<algorithm>
#include<cassert>
#include<iterator>
using namespace std;
using Ull = unsigned long long;
using HashPair = pair<Ull, long long>;
const long long MOD = 1000*1000*1000 + 7;
const Ull MUL = 5;
struct Hash
{
vector<Ull> pow64, hash64;
vector<long long> powP, hashP;
Hash(const string & s = "") : pow64(s.size()+1), hash64(s.size()+1),
powP(s.size()+1), hashP(s.size()+1)
{
hashP[0] = 0;
hash64[0] = 0;
powP[0] = 1;
pow64[0] = 1;
for(int i = 0; i<(int)s.size(); i++)
{
int c;
switch(s[i]){
case '-' : c = 1; break;
case '~' : c = 2; break;
case 'v' : c = 3; break;
default : c = 4;
}
hash64[i+1] = hash64[i]*MUL + c;
hashP[i+1] = (hashP[i]*MUL + c)%MOD;
pow64[i+1] = pow64[i]*MUL;
powP[i+1] = (powP[i]*MUL)%MOD;
}
}
long long getHashP(int l, int r)
{
long long tmp = (hashP[r] - hashP[l-1]*powP[r-l+1])%MOD;
return tmp + (tmp<0?MOD:0);
}
Ull getHash64(int l, int r)
{
return hash64[r] - hash64[l-1]*pow64[r-l+1];
}
HashPair getHash(int l, int r)
{
if(l>r)
return {0,0};
return {getHash64(l,r), getHashP(l,r)};
}
};
struct OpenAddressHashTable
{
static const int size{4*1000*1000 + 37}; //prime number greater than 4'000'000
static const int step{50000};
HashPair cleared{make_pair(0ULL, -1LL)};
vector<HashPair> a{size, cleared};
int hash(const HashPair &toHash) const {
return int((toHash.first + (Ull)toHash.second)%size);
}
int hash2(const HashPair &toHash) const {
return int((toHash.first + (Ull)toHash.second)%step) + 1;
}
void clear()
{
a.assign(size, cleared);
}
void insert(const HashPair & toInsert)
{
int first = hash(toInsert);
int i = first;
int realStep = -1;
while (true)
{
if(a[i] == cleared)
{
a[i] = toInsert;
return;
}
if(a[i] == toInsert)
return;
if (realStep == -1) {
realStep = hash2(toInsert);
}
i+=realStep;
if (i >= size) {
i -= size;
}
assert(i!=first && "NO PLACE");
}
}
int count(const HashPair & toFind)
{
int first = hash(toFind);
int i = first;
int realStep = -1;
while (true)
{
if(a[i] == cleared)
{
return 0;
}
if(a[i] == toFind)
return 1;
if (realStep == -1) {
realStep = hash2(toFind);
}
i+=realStep;
if (i >= size) {
i -= size;
}
assert(i!=first && "NO PLACE");
}
}
};
struct Buckets
{
vector<int> beginnings;
vector<int> next;
vector<HashPair> mem;
int firstUnused{0};
Buckets(int cnt, int capacity) : beginnings(cnt, -1), next(capacity, -1), mem(capacity){}
void clear()
{
beginnings.assign(beginnings.size(), -1);
firstUnused = 0;
}
void push_back(int bucket, const HashPair & toPush)
{
mem[firstUnused] = toPush;
next[firstUnused] = beginnings[bucket];
beginnings[bucket] = firstUnused++;
}
struct Iterator : iterator<std::forward_iterator_tag, HashPair>
{
Buckets & src;
int pos;
Iterator(Buckets & src, int pos = -1) : src(src), pos(pos){};
Iterator & operator ++ ()
{
pos = src.next[pos];
return *this;
}
HashPair & operator *()
{
return src.mem[pos];
}
bool operator == (const Iterator & a)
{
return (&src == &a.src) && (pos == a.pos);
}
bool operator != (const Iterator & a)
{
return (&src != &a.src) || (pos != a.pos);
}
};
Iterator getBegin(int bucket)
{
return Iterator(*this, beginnings[bucket]);
}
Iterator getEnd()
{
return Iterator(*this);
}
};
struct SeparateChainingHashTable
{
static const int bucketsCnt{7*1000*1000};
static const int size{1000*1000 + 5};
Buckets a{bucketsCnt, size};
int hash(const HashPair &toHash) const
{
return int((toHash.first + (Ull)toHash.second)%bucketsCnt);
}
void clear()
{
a.clear();
}
void insert(const HashPair & toInsert)
{
int hashed = hash(toInsert);
if(!any_of(a.getBegin(hashed), a.getEnd(),
[toInsert](const HashPair & toCheck){return toInsert == toCheck;} ))
{
a.push_back(hashed, toInsert);
}
}
int count(const HashPair & toFind)
{
int hashed = hash(toFind);
return any_of(a.getBegin(hashed), a.getEnd(),
[toFind](const HashPair & toCheck){return toFind == toCheck;} );
}
};
void gen_test(int n, std::string& A, std::string& B) {
A.assign(n, 'v');
B.assign(n, 'v');
for (int i = 0, j = n-1; i <= j; ++i,--j) {
A[i] = '~';
B[j] = '-';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
string A, B;
//cin >> A;
//cin >> B;
gen_test(1000000, A, B);
int lLen = 0, rLen = min(A.size(), B.size()) + 1;
Hash hashA(A), hashB(B);
SeparateChainingHashTable hashes;
while(lLen+1<rLen)
{
int mid = (lLen+rLen)/2;
hashes.clear();
for(int i = 0; i+mid <= (int)A.size(); i++)
hashes.insert(hashA.getHash(i+1, i+mid));
bool noThatHash = 1;
for(int i = 0; i + mid <= (int)B.size() && noThatHash; i++)
{
noThatHash = !hashes.count(hashB.getHash(i+1,i+mid));
}
if(noThatHash)
rLen = mid;
else
lLen = mid;
}
cout << lLen << endl;
return 0;
}