#include <bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=(a); i<int(b); ++i)
#define RFOR(i,a,b) for(int i=(b)-1; i>=int(a); --i)
#define rep(i,n) FOR(i,0,n)
#define rep1(i,n) FOR(i,1,int(n)+1)
#define rrep(i, n) RFOR(i, 0, n)
#define rrep1(i, n) RFOR(i, 1, int(n) + 1)
#define all(c) begin(c), end(c)
#define al(d) d.begin(),d.end()
#define fill(n) std::setfill('0')<<std::right<<std::setw(n)
#define intmax 2147483647
#define llmax 9223372036854775807
#define mod 1000000007
template <typename T>
using vec = std::vector<T>;
using vi = vec<int>;
using vvi = vec<vi>;
using pii = std::pair<int, int>;
using ll = long long;
using vll = vec<ll>;
using ld = long double;
#define pair(a,b) "("<<a<<","<<b<<")"
#define pairr(a,b,c) "("<<a<<","<<b<<","<<c<<")"
#define lmin(a,b) (a<b)? a : b

template <typename T>
void putv(vector<T>& V){
	// cout << "The elements in the vector are: " << endl;
	for(auto x: V)
		cout << x << " ";
	cout << endl;
}
template <class T>
vector<T> getv(int n){
	vector<T> vec;
	rep(i,n){
		T input;
		cin >> input;
		vec.emplace_back(input);
	}
	return vec;
}

////Graph
using Weight = ll;
using Flow = int;
struct Edge {
	int src, dst;
	Weight weight;
	Flow cap;
	Edge() : src(0), dst(0), weight(0) {}
	Edge(int s, int d, Weight w) : src(s), dst(d), weight(w) {}
};
using Edges = std::vector<Edge>;
using Graph = std::vector<Edges>;
using Array = std::vector<Weight>;
using Matrix = std::vector<Array>;
void add_edge(Graph &g, int a, int b, Weight w = 1){
	g[a].emplace_back(a, b, w);
	g[b].emplace_back(b, a, w);
}
void add_arc(Graph &g, int a, int b, Weight w = 1){
	g[a].emplace_back(a, b, w);
}
void init_weighted_graph(Graph &g, int N, int M){
	vector<Edge> empty_Edges;
	g = Graph(N,empty_Edges);
	rep(i,M){
		int a,b,w;
		cin >> a >> b >> w;
		add_edge(g,a-1,b-1,w);
	}
// 例えば
//// 3 3
//// 1 2 1
//// 1 3 1
//// 2 3 3
// から
//// The Adjacency List of
//// G=(V,E) (n=3,m=6)
//// 0: (1,1)(2,1)
//// 1: (0,1)(2,3)
//// 2: (0,1)(1,3)
// を得る．
}
void init_weighted_directed_graph(Graph &g, int N, int M){
	vector<Edge> empty_Edges;
	g = Graph(N,empty_Edges);
	rep(i,M){
		int a,b,w;
		cin >> a >> b >> w;
		add_arc(g,a-1,b-1,w);
	}
}
void show_weighted_graph(const Graph &g){
	int N = g.size();
	int M = 0;
	rep(s,N)
		M += g[s].size();
	cout << "///////////////////////"<<endl;
	cout <<"The Adjacency List of" <<endl<< "G=(V,E) (n="<<N<<","<<"m="<<M<<")"<<endl;
	rep(s,N){
		cout << s << ": ";
		int size = g[s].size();
		rep(t,size)
			cout << pair(g[s][t].dst, g[s][t].weight);
		cout << endl;
	}
	cout << "////////////////////////"<<endl<<endl;
}
// a->bが存在するか
bool is_adj(Graph &g, int a, int b){
	for(auto e: g[a])
		if(b==e.dst)
			return true;
	return false;
}

// O(V^3)
Matrix warshallFloyd(const Graph &g) {
	auto const inf = numeric_limits<Weight>::max()/8;
	int n = g.size();
	Matrix d(n,Array(n,inf));
	rep(i,n) d[i][i]=0;
	rep(i, n) for(auto &e: g[i]) d[e.src][e.dst] = min(d[e.src][e.dst],e.weight);
	rep(k,n) rep(i,n) rep(j,n)
		if(d[i][k]!=inf && d[k][j]!=inf) d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
	return d;
}

//////////

void out(bool ans){
	if(ans == true) cout << "yes" << endl;
	else cout << "no" << endl;
}
void Out(bool ans){
	if(ans == true) cout << "Yes" << endl;
	else cout << "No" << endl;
}
void OUT(bool ans){
	if(ans == true) cout << "YES" << endl;
	else cout << "NO" << endl;
}

//// Mathematical functions
ll gcd(const ll a, const ll b){
	return (b==0) ? a : gcd(b,a%b);
}
ll lcm(const ll a, const ll b){
	return a*(b/gcd(a,b));
}
ll Manhattan_dist(ll x1, ll y1, ll x2, ll y2){
	return abs(x1-x2) + abs(y1-y2);
}
// nをpで何回割れるか
ll hmt_p_divides_n(ll p, ll n){
	ll s = 0;
	while(n%p == 0){
		n /= p;
		s++;
	}
	return s;
}
int digit(ll n){
	int cntr = 0;
	while(n>0){
		n/=10;
		cntr++;
	}
	return cntr;
}
// generarized absolute value
template<class T>
T gabs(const T& x){
	return max(x, -x);
}
#define abs gabs
ll memo[100][100] = {};
ll binom(const int n, const int r){
	if(n<r) return 0;
	if(r==0 || n==r) return 1;
	if(memo[n][r]>0) 
		return memo[n][r];
	else
		return memo[n][r] = binom(n-1,r) + binom(n-1,r-1);
}
ll pw(ll x, int y) {
	ll a = 1;
	while(y){
		if(y&1)
			a = (a*x)%mod; 
		x = (x*x)%mod;
		y >>= 1;
	}
	return a;
}
ll modinv(ll x){
	return pw(x, mod-2);
}

bool is_prime(int p){
	if(p<=1)
		return false;
	for(int n=2; n<=pow(p,0.5);++n)
		if(p%n==0)
			return false;
	return true;
}
//n以下の最大の素因数を求める
int max_prime_divisor(int n){
	for(int x=n; x>1; --x)
		if(is_prime(x) && n%x==0)
			return x;
	return -1;
}
vi factorize(int n){
	vi factors;
	while(n>1){
		int p = max_prime_divisor(n);
		factors.emplace_back(p);
		n /= p;
	}
	sort(factors.begin(),factors.end());
	putv(factors);
	return factors;
}

//b進数での各桁の合計
ll f(ll b, ll n){
	if(n<b)
		return n;
	return f(b,n/b) + n%b;
}



//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////


void field_table(int p){
	// 素数pに対して
	// 0..p-1の和と
	// 1..p-1の積テーブルを作る

	cout << "MOD = " << p <<endl<<endl;
	cout << " + | ";
	for(int a=0; a<=p-1; ++a)
		cout <<fill(2)<<a<<" ";
	cout << endl;
	cout << "----";
	for(int i=0;i<p;++i)
		cout << "---";
	cout << endl;
	for(int a=0; a<=p-1; ++a){
		cout <<fill(2)<<a<<" | ";
		for(int b=0; b<=p-1; ++b)
			cout << fill(2)<<(a+b)%p<<" ";
		cout << endl;
	}

	cout << endl << " * | ";
	for(int a=1; a<=p-1; ++a)
		cout <<fill(2)<<a<<" ";
	cout << endl;
	cout << "----";
	for(int i=0;i<p-1;++i)
		cout << "---";
	cout << endl;
	for(int a=1; a<=p-1; ++a){
		cout <<fill(2)<<a<<" | ";
		for(int b=1; b<=p-1; ++b)
			cout << fill(2)<<(a*b)%p<<" ";
		cout << endl;
	}
}


// mod p で見て nのマイナスの逆元を調べる
int minus_inverse(int p, int n){
	int a = p-n;
	for(int b=1; b<p; ++b)
		if((a*b)%p == 1)
			return b;
	return -1;
}
int inverse(int p, int n){
	int a=1;
	while((n*a)%p != 1)
		a++;
	return a;
}
int inverse_minus_inverese(int p, int n){
	int a = inverse(p,n);
	return inverse(p,p-a);
}
// int main(){
// 	int p = 7;

// 	field_table(p);
// 	for(int n=1; n<p; ++n){
// 		cout << n << ": "<< inverse(p,n);
// 		cout << " " << p-inverse(p,n);
// 		cout << " " << inverse_minus_inverese(p,n) << endl;
// 	}

// }

// int main(){
// 	int n,k;
// 	cin >> n >>k;
// 	bitset<17> bs(k);
// 	int pos = 16;
// 	long double pr = 1;
// 	for(int i=0;i<=16;++i)
// 		pr /=2;
// 	long double ret = 0;
// 	for(int i=1;i<=min(n,k);++i){
// 		bs = bitset<17>(k/i);
// 		// cout << k/i << ": "<< bs << endl;
// 		while(!bs[pos]){
// 			pr *= 2;
// 			pos--;
// 		}
// 		// cout << pos << endl;
// 		ret += pr;
// 	}
// 	if(n>k)
// 		ret += n-k;
// 	// cout << ret << endl;
// 	cout << ret/n << endl; 
// }



// a->bの距離が2ということはa->c->b各距離が１のcが存在する
int main(){
	string S;
	cin >> S;

	sort(S.begin(),S.end());
	// cout << S << endl;

	if(S[0]==S[1] && S[1]!=S[2] && S[2]==S[3])
		cout << "Yes" << endl;
	else
		cout << "No" << endl;


}

Measured memory_bytes: 256