//#pragma GCC target("avx2,avx")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
//#include <atcoder/all>
//using namespace atcoder;
//using mint = modint998244353;
//using mint = modint1000000007;
#include <bits/stdc++.h>
using namespace std;
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//using namespace __gnu_pbds;
//using i128 = __int128_t;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pll = pair<long long, long long>;
#define rep(i, n) for(int i = 0; i < (n); ++i)
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
constexpr char ln = '\n';
template<class T1, class T2> inline bool chmax(T1 &a, T2 b) {if (a < b) {a = b; return true;} return false;}
template<class T1, class T2> inline bool chmin(T1 &a, T2 b) {if (a > b) {a = b; return true;} return false;}
inline int topbit(int x) {return x == 0 ? -1 : 31-__builtin_clz(x);}
inline int topbit(long long x) {return x == 0 ? -1 : 63-__builtin_clzll(x);}
inline int botbit(int x) {return x == 0 ? 32 : __builtin_ctz(x);}
inline int botbit(long long x) {return x == 0 ? 64 : __builtin_ctzll(x);}
inline int popcount(int x) {return __builtin_popcount(x);}
inline int popcount(long long x) {return __builtin_popcountll(x);}
inline int kthbit(long long x, int k) {return (x>>k)&1;}
inline void print() {cout << "\n";}
template<class T>
inline void print(const vector<T> &v) {
    for (auto itr = v.begin(); itr != v.end(); ++itr) cout << *itr << " ";
    print();
}
template<class T, class... Args>
inline void print(const T &x, const Args &... args) {
    cout << x << " ";
    print(args...);
}
#ifdef MINATO_LOCAL
#define dump(x) cerr << __LINE__ << " : " << #x << " = " << (x) << endl;
inline void debug() {cerr << endl;}
template<class T>
inline void debug(const vector<T> &v) {
    for (auto itr = v.begin(); itr != v.end(); ++itr) cerr << *itr << " ";
    debug();
}
template<class T, class... Args>
inline void debug(const T &x, const Args &... args) {
    cerr << x << " ";
    debug(args...);
}
#else
#define dump(x) void(0)
inline void debug() {}
template<class T> inline void debug(const vector<T> &v) {}
template<class T, class... Args> inline void debug(const T &x, const Args &... args) {}
#endif
struct Fast_ios {Fast_ios() {cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(20);};} fast_ios;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


template<class C>
struct SuffixAutomaton {
    //各nodeは複数の部分文字列を担当する
    //最長の文字列の長さはnode.len
    //最短の文字列の長さはnode.link.len+1
	struct State {
		int len, link, original;
		map<C, int> nex;
		State(int len, int link, int original) : len(len), link(link), original(original) {}
	};
	vector<State> node;
    vector<int> tid;
	int last;
 
	SuffixAutomaton() : last(0) {node.emplace_back(0,-1,0);}
 
    //文字をcurの直後に追加する
    //各文字に紐づいた値でdpしたいときはdp[last] += val する
	int add(C c, int cur = -1) {
		if (cur == -1) cur = last;
 
		if (node[cur].nex.count(c) && node[node[cur].nex[c]].len == node[cur].len + 1) {
			int q = node[cur].nex[c];
			return last = q;
		}

        int p = cur;
		int nex = node.size();
		node.emplace_back(node[p].len+1,0,nex);
		while (p != -1 && !node[p].nex.count(c)) {
			node[p].nex[c] = nex;
			p = node[p].link;
		}
		if (p == -1) {
            node[nex].link = 0;
			return last = nex;
		}
 
		int q = node[p].nex[c];
		if (node[p].len+1 == node[q].len) {
			node[nex].link = q;	
		} else {
			int clone = node.size();
			node.emplace_back(node[q]);
			node[clone].len = node[p].len + 1;
 
			while (p != -1 && node[p].nex[c] == q) {
				node[p].nex[c] = clone;
				p = node[p].link;
			}
            node[q].link = node[nex].link = clone;
		}
		return last = nex;
	}
 	
	void sortTopologically() {
		vector<int> deg(size());

		for(int i = 0; i < size(); ++i) {
			for(auto &p : node[i].nex) deg[p.second]++;
		}
		queue<int> que;
		que.emplace(0);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			tid.emplace_back(v);
			for(auto &p : node[v].nex) {
				if(--deg[p.second] == 0) que.emplace(p.second);
			}
		}
	}
 
	vector<int> buildSuffixTree() {
		vector<int> ret;
 
		vector<vector<int>> G(size());
		for(int i = 1; i < size(); ++i) G[node[i].link].emplace_back(i);
		queue<int> que;
		que.emplace(0);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			ret.emplace_back(v);
			for(auto nv : G[v]) que.emplace(nv);
		}
		return ret;
	}

	//相異なる部分文字列の個数
    long long numberOfDistinctSubstrings() {
        assert(!tid.empty());
        vector<long long> dp(size());
        dp[0] = 1;
        long long ret = 0;
        for (int i = 0; i < size(); ++i) {
            int u = tid[i];
            ret += dp[u];
            for (auto &p : node[u].nex) {
                dp[p.second] += dp[u];
            }
        }

        return ret-1;
    }

    //SAにある部分文字列が何回出現するか調べられるdpテーブルを返す
    //根から辿った先の添え字の値が出現する回数
    vector<long long> enumNumberOfOccurrences() {
        auto vid = buildSuffixTree();
        vector<long long> dp(size());
        for (int i = size()-1; i > 0; --i) {
            int u = vid[i];
            if (node[u].original==u) dp[u]++;
            dp[node[u].link] += dp[u];
        }
        return dp;
    }

	//部分文字列の初回出現位置 [l,r)のrを返す
	vector<int> enumFirstOccurrences() {
		vector<int> ret(size());
        for (int i = 0; i < size(); ++i) {
            ret[tid[i]] = node[node[tid[i]].original].len;
        }

        return ret;
	}

	//部分文字列の最終出現位置 [l,r)のrを返す
    vector<int> enumLastOccurrences() {
        assert(!tid.empty());
        vector<int> ret(size());
        for (int i = size()-1; i >= 0; --i) {
            int u = tid[i];
            ret[u] = max(ret[u],node[u].len);
            if (i > 0) ret[node[u].link] = max(ret[node[u].link],ret[u]);
        }

        return ret;
    }

	vector<long long> preprocessDistinctSubstring() {
		assert(!tid.empty());
		vector<long long> dp(size(),1);
		for (int i = size()-1; i >= 0; --i) {
			for (auto &j : node[i].nex) {
				dp[i] += dp[j.second];
			}
		}
		return dp;
	}
	
	//辞書順K番目のユニークな部分文字列
	string kthDistinctSubstring(long long K,const vector<long long> &dp) {
		string ret = "";
		if (K <= 0 or K >= dp[0]) return ret;
		int cur = 0;
		while (K > 0) {
			K--;
			for (auto &j : node[cur].nex) {
				if (K - dp[j.second] < 0) {
					ret += j.first;
					cur = j.second;
					break;
				}
				K -= dp[j.second];
			}
		}

		return ret;
	}

    State operator[](int i) const {return node[i];}

	int size() const {return (int)node.size();}
};


int main() {
    string S; cin >> S;
    SuffixAutomaton<char> SA;
    for (int i = 0, cur = 0; i < SZ(S); i++) {
        cur = SA.add(S[i],cur);
    }
    SA.sortTopologically();
    cout << SA.numberOfDistinctSubstrings() << ln;
}


Measured memory_bytes: 112792